Tore everything down and started over

This commit is contained in:
Nate Choe
2022-06-06 11:26:19 -05:00
parent 9d0d998b71
commit ed99ca0223
19 changed files with 211 additions and 1589 deletions

View File

@@ -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

View File

@@ -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
View File

@@ -0,0 +1,5 @@
#define ESCAPE_CHAR '%'
#define INCLUDE_CHAR '@'
#define VAR_CHAR '='
#define MAX_INCLUDE_DEPTH 10

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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
View 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
View 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

View File

@@ -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

View File

@@ -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[] = {
{'<', "&lt;"},
{'>', "&gt;"},
{'&', "&amp;"},
{'"', "&quot;"},
};
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;
}

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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
View 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
View 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;
}

View File

@@ -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;
}