diff --git a/Makefile b/Makefile index a329c49..7aa2571 100644 --- a/Makefile +++ b/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 diff --git a/src/html.c b/src/html.c deleted file mode 100644 index 8a14f7c..0000000 --- a/src/html.c +++ /dev/null @@ -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 . - */ -#include -#include - -#include - -/* truncates groups of whitespace into a single space, removes all whitespace - * immediately before and after <> characters. - * - * Example: " < 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; -} diff --git a/src/include/config.h b/src/include/config.h new file mode 100644 index 0000000..1714176 --- /dev/null +++ b/src/include/config.h @@ -0,0 +1,5 @@ +#define ESCAPE_CHAR '%' +#define INCLUDE_CHAR '@' +#define VAR_CHAR '=' + +#define MAX_INCLUDE_DEPTH 10 diff --git a/src/include/html.h b/src/include/html.h deleted file mode 100644 index c6e0c87..0000000 --- a/src/include/html.h +++ /dev/null @@ -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 . - */ -#ifndef HAVE_HTML -#define HAVE_HTML - -#include - -int copyhtml(FILE *in, FILE *out); -/* Copies the contents of in to out, minimizing html along the way. */ - -#endif diff --git a/src/include/inlines.h b/src/include/inlines.h deleted file mode 100644 index 966272c..0000000 --- a/src/include/inlines.h +++ /dev/null @@ -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 . - */ -#ifndef HAVE_INLINES -#define HAVE_INLINES - -#include - -void writeline(char *data, FILE *out); -void writedata(char *data, size_t len, FILE *out); - -#endif diff --git a/src/include/io.h b/src/include/io.h deleted file mode 100644 index fa87bd1..0000000 --- a/src/include/io.h +++ /dev/null @@ -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 . - */ - -#ifndef HAVE_IO -#define HAVE_IO - -#include - -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 diff --git a/src/include/markdown.h b/src/include/markdown.h deleted file mode 100644 index 0781103..0000000 --- a/src/include/markdown.h +++ /dev/null @@ -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 . - */ -#ifndef HAVE_MARKDOWN -#define HAVE_MARKDOWN - -#include - -int parsemarkdown(FILE *infile, FILE *outfile); - -#endif diff --git a/src/include/mdutil.h b/src/include/mdutil.h deleted file mode 100644 index eb49c92..0000000 --- a/src/include/mdutil.h +++ /dev/null @@ -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 . - */ - -#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 diff --git a/src/include/parse.h b/src/include/parse.h new file mode 100644 index 0000000..969158c --- /dev/null +++ b/src/include/parse.h @@ -0,0 +1,8 @@ +#ifndef HAVE_PARSE +#define HAVE_PARSE + +#include + +int parsefile(char *template, FILE *out); + +#endif diff --git a/src/include/string.h b/src/include/string.h new file mode 100644 index 0000000..8d47fd8 --- /dev/null +++ b/src/include/string.h @@ -0,0 +1,17 @@ +#ifndef HAVE_STRING +#define HAVE_STRING + +#include + +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 diff --git a/src/include/util.h b/src/include/util.h deleted file mode 100644 index 027569e..0000000 --- a/src/include/util.h +++ /dev/null @@ -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 . - */ -#ifndef HAVE_UTIL -#define HAVE_UTIL - -#include - -#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 diff --git a/src/inlines.c b/src/inlines.c deleted file mode 100644 index b46e21d..0000000 --- a/src/inlines.c +++ /dev/null @@ -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 . - */ -#include -#include - -#include -#include -#include - -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("", out); - writeescaped(data + start, end - start, out); - fputs("", 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("= 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 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("= 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("", out); - writeescaped(data + linkstart, linkend - linkstart, out); - fputs("", 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("
", out); - return i; - } - if (i + codelen >= len) - return -1; - if (memcmp(data + i, endcode, codelen) != 0) - return -1; - fputs("
", 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, ""); - 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, "", 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("", out); - for (i = 0; i < count / 2; ++i) - fputs("", out); -} -static void writeclosetags(int count, FILE *out) { - int i; - for (i = 0; i < count / 2; ++i) - fputs("", out); - if (count % 2 != 0) - fputs("", 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; -} diff --git a/src/io.c b/src/io.c deleted file mode 100644 index 670ad66..0000000 --- a/src/io.c +++ /dev/null @@ -1,74 +0,0 @@ -#include -#include - -#include - -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; -} diff --git a/src/main.c b/src/main.c index ef0b3a6..d006dd2 100644 --- a/src/main.c +++ b/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 . - */ -#define _POSIX_C_SOURCE 2 - #include -#include +#include -#include -#include +#include -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); } diff --git a/src/markdown.c b/src/markdown.c deleted file mode 100644 index e944854..0000000 --- a/src/markdown.c +++ /dev/null @@ -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 . - */ -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -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("", 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("

", out); - writedata(currstate->para->data, currstate->para->len, out); - fputs("

", out); - resetstring(currstate->para); - break; - case SETEXT2: - if (currstate->prev.type != PLAIN) - goto hr; - currstate->prev.type = EMPTY; - fputs("

", out); - writedata(currstate->para->data, currstate->para->len, out); - fputs("

", out); - resetstring(currstate->para); - break; - case HR: hr: - endpara(currstate, out); - currstate->prev.type = EMPTY; - fputs("
", 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: - -

Chapter 1


- - * but rather to this - -

Chapter 1

- - * 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("
", 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("", out);
-		}
-		else
-			fputs("
", out); - fputs(realcontent(line, &type), out); - break; - case HEADER: - endpara(currstate, out); - fprintf(out, "", type.data.intensity); - writeline(realcontent(line, &type), out); - fprintf(out, "", 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("

", out); - writedata(state->para->data, state->para->len, out); - fputs("

", out); - resetstring(state->para); - return 0; - case SPACECODE: case FENCECODE: - fputs("
", out); - return 0; - case HEADER: - fprintf(out, "", state->prev.data.intensity); - return 0; - case SETEXT1: - fputs("", out); - break; - case SETEXT2: - fputs("", 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; - } -} diff --git a/src/mdutil.c b/src/mdutil.c deleted file mode 100644 index 40ed1bf..0000000 --- a/src/mdutil.c +++ /dev/null @@ -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 . - */ - -#include -#include -#include - -#include -#include - -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("") != 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; -} diff --git a/src/parse.c b/src/parse.c new file mode 100644 index 0000000..49587ee --- /dev/null +++ b/src/parse.c @@ -0,0 +1,115 @@ +#include +#include + +#include + +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; +} diff --git a/src/string.c b/src/string.c new file mode 100644 index 0000000..8ee7f00 --- /dev/null +++ b/src/string.c @@ -0,0 +1,48 @@ +#include +#include + +#include + +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; +} diff --git a/src/util.c b/src/util.c deleted file mode 100644 index d9dd686..0000000 --- a/src/util.c +++ /dev/null @@ -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 . - */ -#include -#include - -#include - -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; -}