Tore everything down and started over
This commit is contained in:
2
Makefile
2
Makefile
@@ -2,7 +2,7 @@ SRC = $(wildcard src/*.c)
|
||||
OBJ = $(subst .c,.o,$(subst src,work,$(SRC)))
|
||||
LDFLAGS =
|
||||
CFLAGS := -O2 -pipe -Wall -Wpedantic -Wshadow -ansi
|
||||
CFLAGS += -Isrc/include/
|
||||
CFLAGS += -Isrc/include/ -D_POSIX_C_SOURCE=2
|
||||
INSTALLDIR := /usr/bin/
|
||||
OUT = ncdg
|
||||
|
||||
|
||||
58
src/html.c
58
src/html.c
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
ncdg - A program to help generate natechoe.dev
|
||||
Copyright (C) 2022 Nate Choe (natechoe9@gmail.com)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <html.h>
|
||||
|
||||
/* truncates groups of whitespace into a single space, removes all whitespace
|
||||
* immediately before and after <> characters.
|
||||
*
|
||||
* Example: " < text in html> " -> "<text in html>"
|
||||
* */
|
||||
int copyhtml(FILE *in, FILE *out) {
|
||||
int c;
|
||||
int addspace;
|
||||
for (;;) {
|
||||
addspace = 0;
|
||||
c = fgetc(in);
|
||||
if (c == EOF)
|
||||
goto end;
|
||||
if (isspace(c)) {
|
||||
addspace = 1;
|
||||
while (isspace(c)) {
|
||||
c = fgetc(in);
|
||||
if (c == EOF)
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
if (c == '<' || c == '>') {
|
||||
fputc(c, out);
|
||||
while (isspace(c = fgetc(in))) ;
|
||||
ungetc(c, in);
|
||||
}
|
||||
else {
|
||||
if (addspace)
|
||||
fputc(' ', out);
|
||||
fputc(c, out);
|
||||
}
|
||||
}
|
||||
end:
|
||||
fclose(in);
|
||||
return 0;
|
||||
}
|
||||
5
src/include/config.h
Normal file
5
src/include/config.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#define ESCAPE_CHAR '%'
|
||||
#define INCLUDE_CHAR '@'
|
||||
#define VAR_CHAR '='
|
||||
|
||||
#define MAX_INCLUDE_DEPTH 10
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
ncdg - A program to help generate natechoe.dev
|
||||
Copyright (C) 2022 Nate Choe (natechoe9@gmail.com)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef HAVE_HTML
|
||||
#define HAVE_HTML
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
int copyhtml(FILE *in, FILE *out);
|
||||
/* Copies the contents of in to out, minimizing html along the way. */
|
||||
|
||||
#endif
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
ncdg - A program to help generate natechoe.dev
|
||||
Copyright (C) 2022 Nate Choe (natechoe9@gmail.com)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef HAVE_INLINES
|
||||
#define HAVE_INLINES
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
void writeline(char *data, FILE *out);
|
||||
void writedata(char *data, size_t len, FILE *out);
|
||||
|
||||
#endif
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
ncdg - A program to help generate natechoe.dev
|
||||
Copyright (C) 2022 Nate Choe (natechoe9@gmail.com)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef HAVE_IO
|
||||
#define HAVE_IO
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
struct linefile {
|
||||
char *prevline;
|
||||
FILE *file;
|
||||
};
|
||||
|
||||
void ungetline(struct linefile *file, char *line);
|
||||
char *getline(struct linefile *file);
|
||||
struct linefile *newlinefile(FILE *file);
|
||||
void freelinefile(struct linefile *file);
|
||||
|
||||
#endif
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
ncdg - A program to help generate natechoe.dev
|
||||
Copyright (C) 2022 Nate Choe (natechoe9@gmail.com)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef HAVE_MARKDOWN
|
||||
#define HAVE_MARKDOWN
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
int parsemarkdown(FILE *infile, FILE *outfile);
|
||||
|
||||
#endif
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
ncdg - A program to help generate natechoe.dev
|
||||
Copyright (C) 2022 Nate Choe (natechoe9@gmail.com)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef HAVE_MDUTIL
|
||||
#define HAVE_MDUTIL
|
||||
|
||||
enum linetype {
|
||||
EMPTY,
|
||||
PLAIN,
|
||||
SPACECODE,
|
||||
FENCECODE,
|
||||
HR,
|
||||
SETEXT1,
|
||||
/* === */
|
||||
SETEXT2,
|
||||
/* --- */
|
||||
HEADER,
|
||||
|
||||
HTMLCONCRETE,
|
||||
COMMENTLONG,
|
||||
PHP,
|
||||
COMMENTSHORT,
|
||||
CDATA,
|
||||
SKELETON,
|
||||
GENERICTAG
|
||||
};
|
||||
|
||||
#define HTMLSTART HTMLCONCRETE
|
||||
#define HTMLEND GENERICTAG
|
||||
|
||||
struct linedata {
|
||||
enum linetype type;
|
||||
union {
|
||||
int intensity;
|
||||
int islast;
|
||||
} data;
|
||||
};
|
||||
|
||||
void identifyline(char *line, struct linedata *prev, struct linedata *ret);
|
||||
/* prev is almost never used, but sometimes it is. */
|
||||
char *realcontent(char *line, struct linedata *data);
|
||||
int isgenerictag(char *text);
|
||||
/* Identifies open and close tags */
|
||||
|
||||
#endif
|
||||
8
src/include/parse.h
Normal file
8
src/include/parse.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef HAVE_PARSE
|
||||
#define HAVE_PARSE
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
int parsefile(char *template, FILE *out);
|
||||
|
||||
#endif
|
||||
17
src/include/string.h
Normal file
17
src/include/string.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef HAVE_STRING
|
||||
#define HAVE_STRING
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
struct string {
|
||||
size_t len;
|
||||
size_t alloc;
|
||||
char *data;
|
||||
};
|
||||
|
||||
struct string *newstring();
|
||||
int appendchar(struct string *string, char c);
|
||||
void freestring(struct string *string);
|
||||
void resetstring(struct string *string);
|
||||
|
||||
#endif
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
ncdg - A program to help generate natechoe.dev
|
||||
Copyright (C) 2022 Nate Choe (natechoe9@gmail.com)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef HAVE_UTIL
|
||||
#define HAVE_UTIL
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#define LEN(arr) (sizeof(arr) / sizeof *(arr))
|
||||
|
||||
struct string {
|
||||
size_t len;
|
||||
size_t alloc;
|
||||
char *data;
|
||||
};
|
||||
|
||||
struct string *newstring();
|
||||
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);
|
||||
int ispunc(int c);
|
||||
|
||||
#endif
|
||||
534
src/inlines.c
534
src/inlines.c
@@ -1,534 +0,0 @@
|
||||
/*
|
||||
ncdg - A program to help generate natechoe.dev
|
||||
Copyright (C) 2022 Nate Choe (natechoe9@gmail.com)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <util.h>
|
||||
#include <mdutil.h>
|
||||
#include <inlines.h>
|
||||
|
||||
struct escape {
|
||||
char c;
|
||||
char *code;
|
||||
};
|
||||
struct escape escapes[] = {
|
||||
{'<', "<"},
|
||||
{'>', ">"},
|
||||
{'&', "&"},
|
||||
{'"', """},
|
||||
};
|
||||
|
||||
struct deliminfo {
|
||||
int length;
|
||||
unsigned int capabilities;
|
||||
char c;
|
||||
};
|
||||
static const unsigned int canopen = 1 << 0;
|
||||
static const unsigned int canclose = 1 << 1;
|
||||
|
||||
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 int writeimage(char *data, int i, size_t len, FILE *out);
|
||||
static int writeautolink(char *data, int i, size_t len, FILE *out);
|
||||
static int writehardbreak(char *data, int i, size_t len, FILE *out);
|
||||
static int writerawhtml(char *data, int i, size_t len, FILE *out);
|
||||
static int writedelimrun(char *data, int i, size_t len, FILE *out);
|
||||
static int getdeliminfo(char *data, int i, size_t len, struct deliminfo *ret);
|
||||
/* Returns non-zero if i isn't a delimiter */
|
||||
static void writeopentags(int count, FILE *out);
|
||||
static void writeclosetags(int count, FILE *out);
|
||||
static int getlinkinfo(char *data, int i, size_t len,
|
||||
int *textstart, int *textend,
|
||||
int *titlestart, int *titleend,
|
||||
int *linkstart, int *linkend);
|
||||
static void writeescaped(char *data, size_t len, FILE *out);
|
||||
static void writechescape(char c, FILE *out);
|
||||
static int inlinehtmlcase(char *data, char *start, char *end, FILE *out);
|
||||
/* Returns length of raw html */
|
||||
|
||||
void writeline(char *data, FILE *out) {
|
||||
writedata(data, strlen(data), out);
|
||||
}
|
||||
|
||||
void writedata(char *data, size_t len, FILE *out) {
|
||||
int i;
|
||||
for (i = 0; i < len; ++i) {
|
||||
int newi;
|
||||
if ((newi = writecodespan(data, i, len, out)) >= 0)
|
||||
goto special;
|
||||
if ((newi = writelink(data, i, len, out)) >= 0)
|
||||
goto special;
|
||||
if ((newi = writeimage(data, i, len, out)) >= 0)
|
||||
goto special;
|
||||
if ((newi = writeautolink(data, i, len, out)) >= 0)
|
||||
goto special;
|
||||
if ((newi = writehardbreak(data, i, len, out)) >= 0)
|
||||
goto special;
|
||||
if ((newi = writerawhtml(data, i, len, out)) >= 0)
|
||||
goto special;
|
||||
if ((newi = writedelimrun(data, i, len, out)) >= 0)
|
||||
goto special;
|
||||
if (data[i] == '\\') {
|
||||
if (!ispunc(data[i + 1]))
|
||||
writechescape('\\', out);
|
||||
++i;
|
||||
}
|
||||
writechescape(data[i], out);
|
||||
continue;
|
||||
special:
|
||||
i = newi - 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int writecodespan(char *data, int i, size_t len, FILE *out) {
|
||||
int ticklen;
|
||||
int start, end;
|
||||
|
||||
if (data[i] != '`')
|
||||
return -1;
|
||||
for (ticklen = 0; data[i + ticklen] == '`'; ++ticklen) ;
|
||||
|
||||
start = i + ticklen;
|
||||
for (end = start; end < len; ++end) {
|
||||
int thislen;
|
||||
if (data[end] != '`')
|
||||
continue;
|
||||
for (thislen = 0; data[end + thislen] == '`'; ++thislen);
|
||||
if (ticklen == thislen)
|
||||
break;
|
||||
end += thislen - 1;
|
||||
}
|
||||
if (end >= len) {
|
||||
for (i = 0; i < ticklen; ++i)
|
||||
fputc('`', out);
|
||||
return start + ticklen;
|
||||
}
|
||||
|
||||
fputs("<code>", out);
|
||||
writeescaped(data + start, end - start, out);
|
||||
fputs("</code>", out);
|
||||
return end + ticklen;
|
||||
}
|
||||
|
||||
static int writelink(char *data, int i, size_t len, FILE *out) {
|
||||
int textstart, textend, titlestart, titleend, linkstart, linkend;
|
||||
i = getlinkinfo(data, i, len, &textstart, &textend,
|
||||
&titlestart, &titleend,
|
||||
&linkstart, &linkend);
|
||||
if (i < 0)
|
||||
return -1;
|
||||
fputs("<a 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 int writeimage(char *data, int i, size_t len, FILE *out) {
|
||||
int textstart, textend, titlestart, titleend, linkstart, linkend;
|
||||
if (data[i++] != '!')
|
||||
return -1;
|
||||
i = getlinkinfo(data, i, len, &textstart, &textend,
|
||||
&titlestart, &titleend,
|
||||
&linkstart, &linkend);
|
||||
if (i < 0)
|
||||
return -1;
|
||||
fputs("<img src='", out);
|
||||
writeescaped(data + linkstart, linkend - linkstart, out);
|
||||
fputc('\'', out);
|
||||
if (titlestart >= 0) {
|
||||
fputs(" title='", out);
|
||||
writeescaped(data + titlestart, titleend - titlestart, out);
|
||||
fputc('\'', out);
|
||||
}
|
||||
fputs(" alt='", out);
|
||||
writeescaped(data + textstart, textend - textstart, out);
|
||||
fputs("'>", out);
|
||||
return i;
|
||||
}
|
||||
|
||||
static int writeautolink(char *data, int i, size_t len, FILE *out) {
|
||||
int j, linkstart, linkend;
|
||||
if (data[i++] != '<')
|
||||
return -1;
|
||||
linkstart = i;
|
||||
if (!isalpha(data[i]))
|
||||
return -1;
|
||||
for (j = 1; j < 32 && i + j < len; ++j) {
|
||||
char c;
|
||||
c = data[i + j];
|
||||
if (!isalpha(c) && !isdigit(c) && strchr("+.-", c) == NULL)
|
||||
break;
|
||||
}
|
||||
if ((i += j) >= len)
|
||||
return -1;
|
||||
if (data[i] != ':')
|
||||
return -1;
|
||||
for (++i; i < len; ++i) {
|
||||
if (isctrl(data[i]) || strchr(" <>", data[i]) != NULL)
|
||||
break;
|
||||
}
|
||||
if (i >= len || data[i] != '>')
|
||||
return -1;
|
||||
linkend = i++;
|
||||
|
||||
fputs("<a href='", out);
|
||||
writeescaped(data + linkstart, linkend - linkstart, out);
|
||||
fputs("'>", out);
|
||||
writeescaped(data + linkstart, linkend - linkstart, out);
|
||||
fputs("</a>", out);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static int writehardbreak(char *data, int i, size_t len, FILE *out) {
|
||||
const char *endcode = " \n";
|
||||
const int codelen = strlen(endcode);
|
||||
if (data[i] == '\\') {
|
||||
if (++i >= len)
|
||||
return -1;
|
||||
if (data[i++] != '\n')
|
||||
return -1;
|
||||
fputs("<br />", out);
|
||||
return i;
|
||||
}
|
||||
if (i + codelen >= len)
|
||||
return -1;
|
||||
if (memcmp(data + i, endcode, codelen) != 0)
|
||||
return -1;
|
||||
fputs("<br />", out);
|
||||
return i + codelen;
|
||||
}
|
||||
|
||||
static int writerawhtml(char *data, int i, size_t len, FILE *out) {
|
||||
int taglen;
|
||||
data += i;
|
||||
taglen = isgenerictag(data);
|
||||
if (taglen > 0) {
|
||||
fwrite(data, 1, taglen, out);
|
||||
return i + taglen;
|
||||
}
|
||||
|
||||
if (memcmp(data, "<!--", 4) == 0) {
|
||||
char *end, *text;
|
||||
int j;
|
||||
text = data + 4;
|
||||
end = strstr(text, "-->");
|
||||
if (end == NULL)
|
||||
goto notcomment;
|
||||
taglen = end - text;
|
||||
if (text[0] == '>' || memcmp(text, "->", 2) == 0)
|
||||
goto notcomment;
|
||||
if (text[taglen - 1] == '-') {
|
||||
goto notcomment;
|
||||
}
|
||||
for (j = 0; j < taglen - 1; ++j) {
|
||||
if (memcmp(text + j, "--", 2) == 0)
|
||||
goto notcomment;
|
||||
}
|
||||
|
||||
fwrite(data, 1, taglen + 7, out);
|
||||
|
||||
return i + taglen + 7;
|
||||
}
|
||||
notcomment:
|
||||
if ((taglen = inlinehtmlcase(data, "<?", "?>", out)) > 0)
|
||||
return i + taglen;
|
||||
if ((taglen = inlinehtmlcase(data, "<!", ">", out)) > 0)
|
||||
return i + taglen;
|
||||
if ((taglen = inlinehtmlcase(data, "<![CDATA[", "]]>", out)) > 0)
|
||||
return i + taglen;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int writedelimrun(char *data, int i, size_t len, FILE *out) {
|
||||
struct deliminfo startdelim;
|
||||
int end;
|
||||
if (getdeliminfo(data, i, len, &startdelim))
|
||||
return -1;
|
||||
for (end = i + startdelim.length;
|
||||
end < len && startdelim.length > 0; ++end) {
|
||||
struct deliminfo enddelim;
|
||||
if (getdeliminfo(data, end, len, &enddelim))
|
||||
continue;
|
||||
if (startdelim.c != enddelim.c)
|
||||
continue;
|
||||
if (enddelim.capabilities & canclose) {
|
||||
if (startdelim.length == enddelim.length) {
|
||||
int begin;
|
||||
begin = i + startdelim.length;
|
||||
writeopentags(startdelim.length, out);
|
||||
writedata(data + begin, end - begin, out);
|
||||
writeclosetags(startdelim.length, out);
|
||||
return end + enddelim.length;
|
||||
}
|
||||
}
|
||||
else if (enddelim.capabilities & canopen)
|
||||
startdelim.length -= enddelim.length;
|
||||
/* Handles cases like "**a b* c* "*/
|
||||
/* TODO: This does not properly handle cases like "**a b* c* "*/
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int getdeliminfo(char *data, int i, size_t len, struct deliminfo *ret) {
|
||||
const unsigned int flanksleft = 1 << 0;
|
||||
const unsigned int flanksright = 1 << 1;
|
||||
unsigned int flanks;
|
||||
if (data[i] != '*' && data[i] != '_')
|
||||
return 1;
|
||||
ret->c = data[i];
|
||||
for (ret->length = 0; i + ret->length < len &&
|
||||
data[i + ret->length] == ret->c; ++ret->length) ;
|
||||
flanks = 0;
|
||||
if (i != 0) {
|
||||
if (ispunc(data[i - 1])) {
|
||||
if (i + ret->length >= len)
|
||||
flanks |= flanksright;
|
||||
else {
|
||||
char after;
|
||||
after = data[i + ret->length];
|
||||
if (isspace(after) || ispunc(after))
|
||||
flanks |= flanksright;
|
||||
}
|
||||
}
|
||||
else {
|
||||
flanks |= flanksright;
|
||||
}
|
||||
}
|
||||
if (i + ret->length < len) {
|
||||
if (!isspace(data[i + ret->length])) {
|
||||
if (!ispunc(data[i + ret->length]))
|
||||
flanks |= flanksleft;
|
||||
else if (i == 0 || isspace(data[i - 1]) || ispunc(data[i - 1]))
|
||||
flanks |= flanksleft;
|
||||
}
|
||||
}
|
||||
|
||||
ret->capabilities = 0;
|
||||
if (ret->c == '*') {
|
||||
if (flanks & flanksleft)
|
||||
ret->capabilities |= canopen;
|
||||
if (flanks & flanksright)
|
||||
ret->capabilities |= canclose;
|
||||
}
|
||||
else {
|
||||
if (flanks & flanksleft && (!(flanks & flanksright) ||
|
||||
(i > 0 && ispunc(data[i - 1]))))
|
||||
ret->capabilities |= canopen;
|
||||
if (flanks & flanksright && (!(flanks & flanksleft) ||
|
||||
(ret->length + i < len && ispunc(data[i + 1]))))
|
||||
ret->capabilities |= canopen;
|
||||
}
|
||||
/* I have absolutely no clue why the different characters used in
|
||||
* delimiter runs have different meanings, but I absolutely hate that
|
||||
* they do. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void writeopentags(int count, FILE *out) {
|
||||
int i;
|
||||
if (count % 2 != 0)
|
||||
fputs("<em>", out);
|
||||
for (i = 0; i < count / 2; ++i)
|
||||
fputs("<strong>", out);
|
||||
}
|
||||
static void writeclosetags(int count, FILE *out) {
|
||||
int i;
|
||||
for (i = 0; i < count / 2; ++i)
|
||||
fputs("</strong>", out);
|
||||
if (count % 2 != 0)
|
||||
fputs("</em>", out);
|
||||
}
|
||||
|
||||
static int getlinkinfo(char *data, int i, size_t len,
|
||||
int *textstart, int *textend,
|
||||
int *titlestart, int *titleend,
|
||||
int *linkstart, int *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;
|
||||
return i;
|
||||
}
|
||||
|
||||
static void writeescaped(char *data, size_t len, FILE *out) {
|
||||
int i;
|
||||
for (i = 0; i < len; ++i)
|
||||
writechescape(data[i], out);
|
||||
}
|
||||
|
||||
static void writechescape(char c, FILE *out) {
|
||||
int i;
|
||||
for (i = 0; i < sizeof escapes / sizeof *escapes; ++i) {
|
||||
if (escapes[i].c == c) {
|
||||
fputs(escapes[i].code, out);
|
||||
return;
|
||||
}
|
||||
}
|
||||
fputc(c, out);
|
||||
}
|
||||
|
||||
static int inlinehtmlcase(char *data, char *start, char *end, FILE *out) {
|
||||
int i;
|
||||
int len;
|
||||
char *endloc;
|
||||
for (i = 0; start[i] != '\0'; ++i)
|
||||
if (data[i] != start[i])
|
||||
return 0;
|
||||
endloc = strstr(data, end);
|
||||
if (endloc == NULL)
|
||||
return 0;
|
||||
len = endloc - data + strlen(end);
|
||||
fwrite(data, 1, len, out);
|
||||
return len;
|
||||
}
|
||||
74
src/io.c
74
src/io.c
@@ -1,74 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <io.h>
|
||||
|
||||
static char *append(char *str, char c, size_t *len, size_t *alloc);
|
||||
|
||||
void ungetline(struct linefile *file, char *line) {
|
||||
file->prevline = line;
|
||||
}
|
||||
|
||||
char *getline(struct linefile *file) {
|
||||
size_t alloc;
|
||||
size_t len;
|
||||
char *ret;
|
||||
int c;
|
||||
|
||||
if (file->prevline != NULL) {
|
||||
ret = file->prevline;
|
||||
file->prevline = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
c = fgetc(file->file);
|
||||
if (c == EOF)
|
||||
return NULL;
|
||||
ungetc(c, file->file);
|
||||
|
||||
alloc = 80;
|
||||
len = 0;
|
||||
ret = malloc(alloc);
|
||||
if (ret == NULL)
|
||||
return NULL;
|
||||
|
||||
for (;;) {
|
||||
c = fgetc(file->file);
|
||||
if (c == '\n') {
|
||||
ret[len] = '\0';
|
||||
return ret;
|
||||
}
|
||||
ret = append(ret, c, &len, &alloc);
|
||||
if (ret == NULL)
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
struct linefile *newlinefile(FILE *file) {
|
||||
struct linefile *ret;
|
||||
ret = malloc(sizeof *ret);
|
||||
ret->prevline = NULL;
|
||||
ret->file = file;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void freelinefile(struct linefile *file) {
|
||||
free(file->prevline);
|
||||
fclose(file->file);
|
||||
free(file);
|
||||
}
|
||||
|
||||
static char *append(char *str, char c, size_t *len, size_t *alloc) {
|
||||
if (*len >= *alloc) {
|
||||
char *newstr;
|
||||
*alloc *= 2;
|
||||
newstr = realloc(str, *alloc);
|
||||
if (newstr == NULL) {
|
||||
free(str);
|
||||
return NULL;
|
||||
}
|
||||
str = newstr;
|
||||
}
|
||||
str[(*len)++] = c;
|
||||
return str;
|
||||
}
|
||||
81
src/main.c
81
src/main.c
@@ -1,79 +1,32 @@
|
||||
/*
|
||||
ncdg - A program to help generate natechoe.dev
|
||||
Copyright (C) 2022 Nate Choe (natechoe9@gmail.com)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#define _POSIX_C_SOURCE 2
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <html.h>
|
||||
#include <markdown.h>
|
||||
#include <parse.h>
|
||||
|
||||
static void printhelp(FILE *file, char *name);
|
||||
static void printhelp(char *progname);
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int i;
|
||||
char *out;
|
||||
char *template;
|
||||
FILE *outfile;
|
||||
int c;
|
||||
|
||||
out = NULL;
|
||||
if (argc < 2)
|
||||
printhelp(argv[0]);
|
||||
template = argv[1];
|
||||
|
||||
while ((c = getopt(argc, argv, "o:h")) >= 0) {
|
||||
switch (c) {
|
||||
case 'o':
|
||||
out = optarg;
|
||||
break;
|
||||
case 'h':
|
||||
printhelp(stdout, argv[0]);
|
||||
return 0;
|
||||
default:
|
||||
printhelp(stderr, argv[0]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (out == NULL)
|
||||
if (argc < 3)
|
||||
outfile = stdout;
|
||||
else
|
||||
outfile = fopen(out, "w");
|
||||
|
||||
for (i = optind; i < argc; ++i) {
|
||||
FILE *infile;
|
||||
infile = fopen(argv[i], "r");
|
||||
if (infile == NULL) {
|
||||
fprintf(stderr, "Failed to open file %s\n", argv[i]);
|
||||
return 1;
|
||||
}
|
||||
if (parsemarkdown(infile, outfile)) {
|
||||
fprintf(stderr, "Failed to parse file %s\n", argv[i]);
|
||||
return 1;
|
||||
else {
|
||||
outfile = fopen(argv[2], "w");
|
||||
if (outfile == NULL) {
|
||||
fprintf(stderr, "Failed to open file %s for writing\n",
|
||||
argv[2]);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return parsefile(template, outfile);
|
||||
}
|
||||
|
||||
static void printhelp(FILE *file, char *name) {
|
||||
fprintf(file,
|
||||
"Usage: %s [file1.md] [file2.md] ... -o [output]\n", name);
|
||||
fputs(
|
||||
"This program is free software. You can redistribute and/or modify it under\n"
|
||||
"the terms of the GNU General Public License as published by the Free\n"
|
||||
"Software Foundation, either version 3 of the License, or (at your option)\n"
|
||||
"any later version.\n", file);
|
||||
static void printhelp(char *progname) {
|
||||
fprintf(stderr, "Usage: %s [template] (out)\n", progname);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
254
src/markdown.c
254
src/markdown.c
@@ -1,254 +0,0 @@
|
||||
/*
|
||||
ncdg - A program to help generate natechoe.dev
|
||||
Copyright (C) 2022 Nate Choe (natechoe9@gmail.com)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <io.h>
|
||||
#include <util.h>
|
||||
#include <mdutil.h>
|
||||
#include <inlines.h>
|
||||
#include <markdown.h>
|
||||
|
||||
struct parsestate {
|
||||
struct linedata prev;
|
||||
struct string *para;
|
||||
|
||||
int isfirst;
|
||||
int intensity;
|
||||
/* Similar to the intensity field in the linedata struct. Currently
|
||||
* stores the number of backticks used in FENCECODE.*/
|
||||
};
|
||||
|
||||
static int parseline(char *line, struct parsestate *currstate, FILE *out);
|
||||
static int endpara(struct parsestate *state, FILE *out);
|
||||
static void handlehtmlcase(struct linedata *data, struct parsestate *state,
|
||||
char *line, FILE *out);
|
||||
|
||||
int parsemarkdown(FILE *infile, FILE *outfile) {
|
||||
struct linefile *realin;
|
||||
struct parsestate currstate;
|
||||
int code;
|
||||
|
||||
currstate.prev.type = EMPTY;
|
||||
currstate.para = newstring();
|
||||
|
||||
realin = newlinefile(infile);
|
||||
for (;;) {
|
||||
char *currline;
|
||||
currline = getline(realin);
|
||||
if (currline == NULL) {
|
||||
code = 0;
|
||||
break;
|
||||
}
|
||||
if (parseline(currline, &currstate, outfile)) {
|
||||
code = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
endpara(&currstate, outfile);
|
||||
freelinefile(realin);
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
static int parseline(char *line, struct parsestate *currstate, FILE *out) {
|
||||
struct linedata type;
|
||||
|
||||
identifyline(line, &currstate->prev, &type);
|
||||
|
||||
switch (currstate->prev.type) {
|
||||
case FENCECODE:
|
||||
if (type.type == FENCECODE &&
|
||||
type.data.intensity >=
|
||||
currstate->intensity) {
|
||||
currstate->prev.type = EMPTY;
|
||||
fputs("</code></pre>", out);
|
||||
return 0;
|
||||
}
|
||||
fprintf(out, "%s\n", line);
|
||||
currstate->isfirst = 0;
|
||||
return 0;
|
||||
case HTMLCONCRETE:
|
||||
handlehtmlcase(&type, currstate, line, out);
|
||||
return 0;
|
||||
case COMMENTLONG:
|
||||
handlehtmlcase(&type, currstate, line, out);
|
||||
return 0;
|
||||
case PHP:
|
||||
handlehtmlcase(&type, currstate, line, out);
|
||||
return 0;
|
||||
case COMMENTSHORT:
|
||||
handlehtmlcase(&type, currstate, line, out);
|
||||
return 0;
|
||||
case CDATA:
|
||||
handlehtmlcase(&type, currstate, line, out);
|
||||
return 0;
|
||||
case SKELETON:
|
||||
handlehtmlcase(&type, currstate, line, out);
|
||||
return 0;
|
||||
case GENERICTAG:
|
||||
handlehtmlcase(&type, currstate, line, out);
|
||||
return 0;
|
||||
case EMPTY: case PLAIN: case SPACECODE: case HR:
|
||||
case SETEXT1: case SETEXT2: case HEADER:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (type.type) {
|
||||
case EMPTY:
|
||||
endpara(currstate, out);
|
||||
currstate->prev.type = EMPTY;
|
||||
break;
|
||||
case SETEXT1:
|
||||
if (currstate->prev.type != PLAIN)
|
||||
return 1;
|
||||
currstate->prev.type = EMPTY;
|
||||
fputs("<h1>", out);
|
||||
writedata(currstate->para->data, currstate->para->len, out);
|
||||
fputs("</h1>", out);
|
||||
resetstring(currstate->para);
|
||||
break;
|
||||
case SETEXT2:
|
||||
if (currstate->prev.type != PLAIN)
|
||||
goto hr;
|
||||
currstate->prev.type = EMPTY;
|
||||
fputs("<h2>", out);
|
||||
writedata(currstate->para->data, currstate->para->len, out);
|
||||
fputs("</h2>", out);
|
||||
resetstring(currstate->para);
|
||||
break;
|
||||
case HR: hr:
|
||||
endpara(currstate, out);
|
||||
currstate->prev.type = EMPTY;
|
||||
fputs("<hr>", out);
|
||||
break;
|
||||
case PLAIN:
|
||||
if (currstate->prev.type != PLAIN) {
|
||||
endpara(currstate, out);
|
||||
currstate->prev.type = PLAIN;
|
||||
}
|
||||
else
|
||||
appendcharstring(currstate->para, '\n');
|
||||
appendstrstring(currstate->para, realcontent(line, &type));
|
||||
break;
|
||||
/* According to the commonmark spec, this markdown:
|
||||
|
||||
Chapter 1
|
||||
---
|
||||
|
||||
* Should NOT compile to this:
|
||||
|
||||
<p>Chapter 1</p><hr />
|
||||
|
||||
* but rather to this
|
||||
|
||||
<h2>Chapter 1</h2>
|
||||
|
||||
* This means that we need to store the contents of the
|
||||
* paragraph and only write after obtaining the whole thing
|
||||
* as to not include the wrong tags.
|
||||
* */
|
||||
case FENCECODE:
|
||||
fputs("<pre><code>", out);
|
||||
currstate->prev.type = FENCECODE;
|
||||
currstate->isfirst = 1;
|
||||
currstate->intensity = type.data.intensity;
|
||||
break;
|
||||
case SPACECODE:
|
||||
if (currstate->prev.type != SPACECODE) {
|
||||
endpara(currstate, out);
|
||||
currstate->prev.type = SPACECODE;
|
||||
fputs("<code class='block'>", out);
|
||||
}
|
||||
else
|
||||
fputs("<br>", out);
|
||||
fputs(realcontent(line, &type), out);
|
||||
break;
|
||||
case HEADER:
|
||||
endpara(currstate, out);
|
||||
fprintf(out, "<h%d>", type.data.intensity);
|
||||
writeline(realcontent(line, &type), out);
|
||||
fprintf(out, "</h%d>", type.data.intensity);
|
||||
currstate->prev.type = EMPTY;
|
||||
break;
|
||||
case HTMLCONCRETE:
|
||||
handlehtmlcase(&type, currstate, line, out);
|
||||
break;
|
||||
case COMMENTLONG:
|
||||
handlehtmlcase(&type, currstate, line, out);
|
||||
break;
|
||||
case PHP:
|
||||
handlehtmlcase(&type, currstate, line, out);
|
||||
break;
|
||||
case COMMENTSHORT:
|
||||
handlehtmlcase(&type, currstate, line, out);
|
||||
break;
|
||||
case CDATA:
|
||||
handlehtmlcase(&type, currstate, line, out);
|
||||
break;
|
||||
case SKELETON:
|
||||
handlehtmlcase(&type, currstate, line, out);
|
||||
break;
|
||||
case GENERICTAG:
|
||||
handlehtmlcase(&type, currstate, line, out);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int endpara(struct parsestate *state, FILE *out) {
|
||||
switch (state->prev.type) {
|
||||
case EMPTY: case HR:
|
||||
case HTMLCONCRETE: case COMMENTLONG: case PHP: case COMMENTSHORT:
|
||||
case CDATA: case SKELETON: case GENERICTAG:
|
||||
return 0;
|
||||
case PLAIN:
|
||||
fputs("<p>", out);
|
||||
writedata(state->para->data, state->para->len, out);
|
||||
fputs("</p>", out);
|
||||
resetstring(state->para);
|
||||
return 0;
|
||||
case SPACECODE: case FENCECODE:
|
||||
fputs("</code>", out);
|
||||
return 0;
|
||||
case HEADER:
|
||||
fprintf(out, "</h%d>", state->prev.data.intensity);
|
||||
return 0;
|
||||
case SETEXT1:
|
||||
fputs("</h1>", out);
|
||||
break;
|
||||
case SETEXT2:
|
||||
fputs("</h2>", out);
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void handlehtmlcase(struct linedata *data, struct parsestate *state,
|
||||
char *line, FILE *out) {
|
||||
endpara(state, out);
|
||||
fputs(line, out);
|
||||
fputc('\n', out);
|
||||
state->prev.type = data->type;
|
||||
if (state->prev.type == data->type && data->data.islast) {
|
||||
state->prev.type = EMPTY;
|
||||
return;
|
||||
}
|
||||
}
|
||||
307
src/mdutil.c
307
src/mdutil.c
@@ -1,307 +0,0 @@
|
||||
/*
|
||||
ncdg - A program to help generate natechoe.dev
|
||||
Copyright (C) 2022 Nate Choe (natechoe9@gmail.com)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <util.h>
|
||||
#include <mdutil.h>
|
||||
|
||||
static char *truncate(char *str);
|
||||
static char *after(char *begin, char *str);
|
||||
static int isend(char *line, enum linetype prev);
|
||||
|
||||
static char *concretetags[] = { "pre", "script", "style", "textarea" };
|
||||
static char *skeletontags[] = {
|
||||
"address", "article", "aside", "base", "basefont", "blockquote", "body",
|
||||
"caption", "center", "col", "colgroup", "dd", "details", "dialog",
|
||||
"dir", "div", "dl", "dt", "fieldset", "figcaption", "figure", "footer",
|
||||
"form", "frame", "frameset", "h1", "h2", "h3", "h4", "h5", "h6", "head",
|
||||
"header", "hr", "html", "iframe", "legend", "li", "link", "main",
|
||||
"menu", "menuitem", "nav", "noframes", "ol", "optgroup", "option", "p",
|
||||
"param", "section", "source", "summary", "table", "tbody", "td",
|
||||
"tfoot", "th", "thead", "title", "tr", "track", "ul",
|
||||
};
|
||||
|
||||
void identifyline(char *line, struct linedata *prev, struct linedata *ret) {
|
||||
int i;
|
||||
if (HTMLSTART <= prev->type && prev->type <= HTMLEND) {
|
||||
ret->type = prev->type;
|
||||
ret->data.islast = isend(truncate(line), prev->type);
|
||||
return;
|
||||
}
|
||||
if (prev->type != PLAIN) {
|
||||
for (i = 0; i < 4; ++i) {
|
||||
if (line[i] == '\t')
|
||||
break;
|
||||
if (line[i] != ' ')
|
||||
goto notspacecode;
|
||||
}
|
||||
ret->type = SPACECODE;
|
||||
return;
|
||||
}
|
||||
notspacecode:
|
||||
line = truncate(line);
|
||||
if (line[0] == '\0') {
|
||||
ret->type = EMPTY;
|
||||
return;
|
||||
}
|
||||
{
|
||||
int hrcount;
|
||||
if (strchr("-*_=", line[0]) == NULL)
|
||||
goto nothr;
|
||||
/* A delimiting line can only contain '-', '*', '_', and ' '. */
|
||||
hrcount = 0;
|
||||
for (i = 0; line[i]; ++i) {
|
||||
if (!isspace(line[i]) && line[i] != line[0])
|
||||
goto nothr;
|
||||
/* You can't mix delimiter characters, and you can't
|
||||
* have anything other than a delimiter character or
|
||||
* white space. */
|
||||
if (line[i] == line[0])
|
||||
++hrcount;
|
||||
}
|
||||
if (hrcount >= 3) {
|
||||
switch (line[0]) {
|
||||
case '=':
|
||||
ret->type = SETEXT1;
|
||||
return;
|
||||
case '-':
|
||||
ret->type = SETEXT2;
|
||||
return;
|
||||
default:
|
||||
ret->type = HR;
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* There has to be at least 3 delimiter characters */
|
||||
}
|
||||
nothr:
|
||||
for (i = 0; line[i] == '`'; ++i) ;
|
||||
if (i >= 3) {
|
||||
ret->type = FENCECODE;
|
||||
ret->data.intensity = i;
|
||||
/* The last line of a fenced code block must have at least the
|
||||
* same number of backticks as the first. */
|
||||
return;
|
||||
}
|
||||
/* notfencedcode: */
|
||||
|
||||
if (line[0] == '#') {
|
||||
int pcount;
|
||||
for (pcount = 0; line[pcount] == '#'; ++pcount) ;
|
||||
if (line[pcount] != ' ' && line[pcount] != '\0')
|
||||
goto notheader;
|
||||
ret->type = HEADER;
|
||||
ret->data.intensity = pcount;
|
||||
return;
|
||||
}
|
||||
|
||||
notheader:
|
||||
|
||||
#define HTMLSTARTCASE(start, rettype) \
|
||||
if (after(start, line) != NULL) { \
|
||||
ret->type = rettype; \
|
||||
ret->data.islast = isend(line, rettype); \
|
||||
return; \
|
||||
}
|
||||
HTMLSTARTCASE("<!--", COMMENTLONG);
|
||||
HTMLSTARTCASE("<![CDATA[", CDATA);
|
||||
HTMLSTARTCASE("<?", PHP);
|
||||
HTMLSTARTCASE("<!", COMMENTSHORT);
|
||||
|
||||
if (line[0] == '<') {
|
||||
char *testline;
|
||||
testline = line + 1;
|
||||
if (testline[0] == '/')
|
||||
++testline;
|
||||
for (i = 0; i < LEN(concretetags); ++i) {
|
||||
char *aftertag;
|
||||
aftertag = after(concretetags[i], testline);
|
||||
if (aftertag == NULL)
|
||||
continue;
|
||||
if (aftertag[0] == '\0' || strchr(" >", aftertag[0])) {
|
||||
ret->type = HTMLCONCRETE;
|
||||
ret->data.islast = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < LEN(skeletontags); ++i) {
|
||||
char *aftertag;
|
||||
aftertag = after(skeletontags[i], testline);
|
||||
if (aftertag == NULL)
|
||||
continue;
|
||||
if (aftertag[0] == '\0' ||
|
||||
strchr(" \t>", aftertag[0]) ||
|
||||
after("/>", aftertag) != NULL) {
|
||||
ret->type = SKELETON;
|
||||
ret->data.islast = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (isgenerictag(line)) {
|
||||
ret->type = GENERICTAG;
|
||||
ret->data.islast = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ret->type = PLAIN;
|
||||
ret->data.islast = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
char *realcontent(char *line, struct linedata *data) {
|
||||
switch (data->type) {
|
||||
case EMPTY: case HR: case SETEXT1: case SETEXT2: case FENCECODE:
|
||||
case HTMLCONCRETE: case COMMENTLONG: case PHP: case CDATA:
|
||||
case SKELETON: case COMMENTSHORT: case GENERICTAG:
|
||||
return NULL;
|
||||
case PLAIN:
|
||||
return line;
|
||||
case SPACECODE:
|
||||
return line + 4;
|
||||
case HEADER:
|
||||
return truncate(line + data->data.intensity);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int isgenerictag(char *text) {
|
||||
int isopen;
|
||||
char *initialtext;
|
||||
initialtext = text;
|
||||
if (text[0] != '<')
|
||||
return 0;
|
||||
++text;
|
||||
if (text[0] == '/') {
|
||||
++text;
|
||||
isopen = 0;
|
||||
}
|
||||
else
|
||||
isopen = 1;
|
||||
|
||||
/* < || </ */
|
||||
|
||||
if (!isalpha(text[0]))
|
||||
return 0;
|
||||
while (isalnum(text[0]) || text[0] == '-')
|
||||
++text;
|
||||
|
||||
/* <[tag name] || </[tag name] */
|
||||
|
||||
for (;;) {
|
||||
char *newtext;
|
||||
newtext = truncate(text);
|
||||
|
||||
if (!isalpha(newtext[0]) &&
|
||||
strchr("_:", newtext[0]) == NULL)
|
||||
break;
|
||||
++newtext;
|
||||
while (isalnum(newtext[0]) ||
|
||||
strchr("_.:-", newtext[0]) != NULL)
|
||||
++newtext;
|
||||
/* Swallow attribute name */
|
||||
|
||||
newtext = truncate(newtext);
|
||||
if (newtext[0] == '=') {
|
||||
char start;
|
||||
++newtext;
|
||||
newtext = truncate(newtext);
|
||||
start = newtext[0];
|
||||
switch (start) {
|
||||
case '\'': case '"':
|
||||
while (newtext[0] != start &&
|
||||
newtext[0] != '\0')
|
||||
++newtext;
|
||||
if (newtext[0] == '\0')
|
||||
return 0;
|
||||
break;
|
||||
/* Swallow single/double quoted attribute
|
||||
* value */
|
||||
default:
|
||||
while (strchr("\"'=<>`", newtext[0])
|
||||
== NULL &&
|
||||
newtext[0] != '\0')
|
||||
++newtext;
|
||||
if (newtext[0] == '\0')
|
||||
return 0;
|
||||
break;
|
||||
/* Swallow unquoted attribute value */
|
||||
}
|
||||
}
|
||||
/* Swallow attribute value */
|
||||
|
||||
text = newtext;
|
||||
}
|
||||
|
||||
/* <[tag name][attribute]* || </tag name][attribute]* */
|
||||
if (isopen && text[0] == '/')
|
||||
++text;
|
||||
if (text[0] == '>')
|
||||
return text - initialtext + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *truncate(char *str) {
|
||||
while (isspace(str[0]))
|
||||
++str;
|
||||
return str;
|
||||
}
|
||||
|
||||
static char *after(char *begin, char *str) {
|
||||
int i;
|
||||
for (i = 0; begin[i]; ++i) {
|
||||
if (begin[i] != str[0])
|
||||
return NULL;
|
||||
++str;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
static int isend(char *line, enum linetype prev) {
|
||||
int i;
|
||||
|
||||
switch (prev) {
|
||||
case EMPTY: case PLAIN: case SPACECODE: case FENCECODE: case HR:
|
||||
case SETEXT1: case SETEXT2: case HEADER:
|
||||
return 1;
|
||||
/* In this case, something has gone terribly wrong. */
|
||||
|
||||
case HTMLCONCRETE:
|
||||
for (i = 0; i < LEN(concretetags); ++i) {
|
||||
char endtag[30];
|
||||
sprintf(endtag, "</%s>", concretetags[i]);
|
||||
return strstr(line, endtag) != NULL;
|
||||
}
|
||||
return 0;
|
||||
case COMMENTLONG:
|
||||
return strstr(line, "-->") != NULL;
|
||||
case PHP:
|
||||
return strstr(line, "?>") != NULL;
|
||||
case COMMENTSHORT:
|
||||
return strchr(line, '>') != NULL;
|
||||
case CDATA:
|
||||
return strstr(line, "]]>") != NULL;
|
||||
case SKELETON: case GENERICTAG:
|
||||
return line[0] == '\0';
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
115
src/parse.c
Normal file
115
src/parse.c
Normal file
@@ -0,0 +1,115 @@
|
||||
#include <parse.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <config.h>
|
||||
|
||||
static int expandfile(struct string *expanded, char *filename, int level);
|
||||
static struct string *getstring(FILE *file, char end);
|
||||
|
||||
int parsefile(char *template, FILE *out) {
|
||||
struct string *expanded;
|
||||
expanded = newstring();
|
||||
if (expanded == NULL)
|
||||
return 1;
|
||||
if (expandfile(expanded, template, 0))
|
||||
return 1;
|
||||
fwrite(expanded->data, 1, expanded->len, stdout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int expandfile(struct string *expanded, char *filename, int level) {
|
||||
FILE *file;
|
||||
int c, linenum;
|
||||
file = fopen(filename, "r");
|
||||
if (file == NULL) {
|
||||
fprintf(stderr, "Failed to open file %s\n", filename);
|
||||
return 1;
|
||||
}
|
||||
linenum = 1;
|
||||
if (level >= MAX_INCLUDE_DEPTH) {
|
||||
fprintf(stderr, "The include depth has reached %d, quitting\n",
|
||||
level);
|
||||
fclose(file);
|
||||
return 1;
|
||||
}
|
||||
for (;;) {
|
||||
c = fgetc(file);
|
||||
switch (c) {
|
||||
case ESCAPE_CHAR:
|
||||
c = fgetc(file);
|
||||
switch (c) {
|
||||
case ESCAPE_CHAR:
|
||||
if (appendchar(expanded, ESCAPE_CHAR))
|
||||
goto error;
|
||||
break;
|
||||
case VAR_CHAR:
|
||||
if (appendchar(expanded, ESCAPE_CHAR))
|
||||
goto error;
|
||||
for (;;) {
|
||||
if (c == EOF)
|
||||
goto error;
|
||||
if (appendchar(expanded, c))
|
||||
goto error;
|
||||
if (c == ESCAPE_CHAR)
|
||||
break;
|
||||
c = fgetc(file);
|
||||
}
|
||||
break;
|
||||
case INCLUDE_CHAR: {
|
||||
struct string *inclname;
|
||||
inclname = getstring(file, ESCAPE_CHAR);
|
||||
if (inclname == NULL)
|
||||
goto error;
|
||||
if (expandfile(expanded, inclname->data,
|
||||
level + 1))
|
||||
return 1;
|
||||
freestring(inclname);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
fprintf(stderr, "Line %d: Invalid escape\n",
|
||||
linenum);
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
case '\n':
|
||||
++linenum;
|
||||
goto casedefault;
|
||||
case EOF:
|
||||
goto end;
|
||||
default: casedefault:
|
||||
if (appendchar(expanded, c))
|
||||
goto error;
|
||||
break;
|
||||
}
|
||||
}
|
||||
end:
|
||||
fclose(file);
|
||||
return 0;
|
||||
error:
|
||||
fclose(file);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct string *getstring(FILE *file, char end) {
|
||||
struct string *ret;
|
||||
int c;
|
||||
ret = newstring();
|
||||
if (ret == NULL)
|
||||
return NULL;
|
||||
for (;;) {
|
||||
c = fgetc(file);
|
||||
if (c == EOF)
|
||||
goto error;
|
||||
if (c == end) {
|
||||
if (appendchar(ret, '\0'))
|
||||
goto error;
|
||||
return ret;
|
||||
}
|
||||
if (appendchar(ret, c))
|
||||
goto error;
|
||||
}
|
||||
error:
|
||||
freestring(ret);
|
||||
return NULL;
|
||||
}
|
||||
48
src/string.c
Normal file
48
src/string.c
Normal file
@@ -0,0 +1,48 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
struct string *newstring() {
|
||||
struct string *ret;
|
||||
ret = malloc(sizeof *ret);
|
||||
if (ret == NULL)
|
||||
goto error1;
|
||||
ret->len = 0;
|
||||
ret->alloc = 5;
|
||||
/* TODO: Change this to a far reasonable number */
|
||||
ret->data = malloc(ret->alloc);
|
||||
if (ret->data == NULL)
|
||||
goto error2;
|
||||
return ret;
|
||||
error2:
|
||||
free(ret);
|
||||
error1:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int appendchar(struct string *string, char c) {
|
||||
if (string->len >= string->alloc) {
|
||||
char *newdata;
|
||||
size_t newalloc;
|
||||
newalloc = string->alloc;
|
||||
while (string->len >= newalloc)
|
||||
newalloc *= 2;
|
||||
newdata = realloc(string->data, newalloc);
|
||||
if (newdata == NULL)
|
||||
return 1;
|
||||
string->data = newdata;
|
||||
string->alloc = newalloc;
|
||||
}
|
||||
string->data[string->len++] = c;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void freestring(struct string *string) {
|
||||
free(string->data);
|
||||
free(string);
|
||||
}
|
||||
|
||||
void resetstring(struct string *string) {
|
||||
string->len = 0;
|
||||
}
|
||||
87
src/util.c
87
src/util.c
@@ -1,87 +0,0 @@
|
||||
/*
|
||||
ncdg - A program to help generate natechoe.dev
|
||||
Copyright (C) 2022 Nate Choe (natechoe9@gmail.com)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <util.h>
|
||||
|
||||
struct string *newstring() {
|
||||
struct string *ret;
|
||||
ret = malloc(sizeof *ret);
|
||||
if (ret == NULL)
|
||||
return NULL;
|
||||
ret->len = 0;
|
||||
ret->alloc = 20;
|
||||
ret->data = malloc(ret->alloc);
|
||||
if (ret->data == NULL)
|
||||
return NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void freestring(struct string *str) {
|
||||
free(str->data);
|
||||
free(str);
|
||||
}
|
||||
|
||||
int appendcharstring(struct string *str, char c) {
|
||||
if (str->len >= str->alloc) {
|
||||
char *newdata;
|
||||
size_t newalloc;
|
||||
newalloc = str->alloc * 2;
|
||||
newdata = realloc(str->data, newalloc);
|
||||
if (newdata == NULL) {
|
||||
return 1;
|
||||
}
|
||||
str->data = newdata;
|
||||
str->alloc = newalloc;
|
||||
}
|
||||
str->data[str->len++] = c;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int appendstrstring(struct string *str, char *s) {
|
||||
size_t len;
|
||||
len = strlen(s);
|
||||
if (str->len + len >= str->alloc) {
|
||||
char *newdata;
|
||||
size_t newalloc;
|
||||
newalloc = str->alloc;
|
||||
while (str->len + len >= newalloc)
|
||||
newalloc *= 2;
|
||||
newdata = realloc(str->data, newalloc);
|
||||
if (newdata == NULL)
|
||||
return 1;
|
||||
str->data = newdata;
|
||||
str->alloc = newalloc;
|
||||
}
|
||||
memcpy(str->data + str->len, s, len);
|
||||
str->len += len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void resetstring(struct string *str) {
|
||||
str->len = 0;
|
||||
}
|
||||
|
||||
int isctrl(int c) {
|
||||
return (0 <= c && c <= 0x1f) || c == 0x7f;
|
||||
}
|
||||
|
||||
int ispunc(int c) {
|
||||
return strchr("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", c) != NULL;
|
||||
}
|
||||
Reference in New Issue
Block a user