adonis-ssg

dead simple, C89 static site generator with markdown support and no external dependencies
git clone git://kqueue.dev/adonis-ssg.git
Log | Files | Refs | README | LICENSE

commit 42d906cff9d3d006c19003afc6c023bb25f0a26f
Author: kqueue <kqueue@cocaine.ninja>
Date:   Mon, 26 Dec 2022 11:01:01 -0500

deleted old commits, rebranded

Diffstat:
ALICENSE | 5+++++
AMakefile | 16++++++++++++++++
AREADME.md | 32++++++++++++++++++++++++++++++++
Aadonis-ssg.c | 152+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aconfig.h | 13+++++++++++++
Aexamples/README.md | 5+++++
Aexamples/conf | 2++
Aexamples/example.md | 5+++++
Aexamples/example2.md | 2++
Aexamples/template.html | 29+++++++++++++++++++++++++++++
Alibsmu.c | 683+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amkfile | 5+++++
12 files changed, 949 insertions(+), 0 deletions(-)

diff --git a/LICENSE b/LICENSE @@ -0,0 +1,5 @@ +Copyright 2020-2023 kqueue <kqueue@cocaine.ninja> + +Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/Makefile b/Makefile @@ -0,0 +1,16 @@ +PREFIX ?= /usr/local +CC ?= cc +INSTALL ?= install + +all: + $(CC) adonis-ssg.c libsmu.c -o adonis-ssg -static -march=native -std=c89 -Os +install: + $(INSTALL) -d $(PREFIX)/bin + $(INSTALL) adonis-ssg $(PREFIX)/bin/ +clean: + rm -f adonis-ssg + rm -f examples/example.html + rm -f examples/example2.html + rm -f examples/rss.xml +uninstall: + rm -f $(PREFIX)/bin/adonis-ssg diff --git a/README.md b/README.md @@ -0,0 +1,32 @@ +This is an old project I wrote around early 2020. I decided to delete all the old git commits though, this is the project that taught me a lot of the basics of programming. +This projects powers kqueue.dev, and is still quite a good static site generator. Very simple, fast, and easy to use. +It's named after adonis, a greek myth. +# Installation +to install adonis-ssg, just run: +``` +make +doas make install +``` +# Usage +## template.html +You first need to write a template.html to use adonis-ssg. +This is pretty much just the template for all the html files adonis-ssg writes. +A good example of this is in examples/template.html. + +For titles, you should add +``` +<title>INSERT-TITLE</title> +``` +to your template.html + +adonis-ssg finds INSERT-TITLE in your template.html, and replaces it with whatever title you put in files.conf +## files.conf +The config file is pretty much TSV +The first option you need to set is markdown input file, then the title for the article, then html output file. Each option is seperated by "\t" +check the conf file in the examples directory for an example of how this looks +## actually running adonis-ssg +just run +``` +adonis-ssg files.conf +``` +and it will generate everything diff --git a/adonis-ssg.c b/adonis-ssg.c @@ -0,0 +1,152 @@ +/* Copyright 2020-2023 kqueue <kqueue@cocaine.ninja> + +Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#if defined(__Openbsd__) +#include <unistd.h> +#endif +#include "config.h" +int smu_convert(FILE * out, FILE * source, int supresshtml); +/* function to find and replac the title */ +char * +replaceword(const char *s, const char *oldW, + const char *newW) +{ + char *result; + int i , cnt = 0; + int newWlen = strlen(newW); + int oldWlen = strlen(oldW); + for (i = 0; s[i] != '\0'; i++) { + if (strstr(&s[i], oldW) == &s[i]) { + cnt++; + i += oldWlen - 1; + } + } + result = (char *) malloc(i + cnt * (newWlen - oldWlen) + 1); + i = 0; + while (*s) { + if (strstr(s, oldW) == s) { + strcpy(&result[i], newW); + i += newWlen; + s += oldWlen; + } else + result[i++] = *s++; + } + + result[i] = '\0'; + return result; +} + +void +generate_files(char *line) +{ + /* declare and open the template file */ + FILE *template; + template = fopen(htmltemplate, "r"); + fseek(template, 0, SEEK_END); + /* get the template size */ + long templatesize = ftell(template); + fseek(template, 0, SEEK_SET); + /* allocate the memory for reading the template file */ + char *file = malloc(templatesize + 1); + /* read the template file */ + fread(file, 1, templatesize, template); + fclose(template); + + char *token, *stuff[3]; + int i = 0; + token = strtok(line, "\t"); + while (token != NULL) { + stuff[i] = token; + token = strtok(NULL, "\t"); + i++; + } + /* find "INSERT-TITLE" and replace it with the specified title */ + char *result = replaceword(file, "INSERT-TITLE", stuff[1]); + free(file); + FILE *article; + article = fopen(stuff[2], "w+"); + fprintf(article, "%s", result); + free(result); + fclose(article); + + FILE *article2, *markdown; + article2 = fopen(stuff[2], "a+"); + markdown = fopen(stuff[0], "rw+"); + smu_convert(article2, markdown, 1); + if(genrss){ + fseek(markdown, 0, SEEK_END); + long fpsize = ftell(markdown); + fseek(markdown, 0, SEEK_SET); + char *file = malloc(fpsize + 1); + fread(file, 1, fpsize, markdown); + FILE *fp = fopen("rss.xml", "a+"); + fprintf(fp, "\n\t<item>\n\t\t<title>%s</title>\n\t\t<link>%s%s</link>\n\t\t<description><![CDATA[<pre>\n%s\n</pre>]]></description>\n\t</item>", stuff[1], url, stuff[2] + 1, file); + free(file); + fclose (fp); + } + rewind(article2); + fputs(appendhtml, article2); + fclose(article2); + fclose(markdown); +} + +int +main(int argc, char *argv[]) +{ +#if defined(__Openbsd__) + pledge("stdio rpath wpath", NULL); +#endif + if (argc < 2) { + printf("usage:\nadonis-ssg files.conf\n"); + return 0; + } + FILE *fp; + /* open file */ + fp = fopen(argv[1], "r"); + /* nc is the number of lines in the file */ + int nc , exit, wc, y; + exit = nc = 0; + while ((y = fgetc(fp)) != EOF) { + if (y == '\n') + nc++; + } + rewind(fp); + if(genrss){ + FILE *rss = fopen("rss.xml", "w"); + fprintf(rss, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\">\n<channel>\n<atom:link href=\"%s/rss.xml\" rel=\"self\" type=\"application/rss+xml\"/>\n\t<link>%s</link>\n\t<title>%s</title>\n\t<description>%s</description>\n\t<generator>https://kqueue.dev/git/adonis-ssg</generator>\n", url, url, rsstitle, rssdesc); + fclose(rss); + } + char c [200]; + wc = nc - 1; + fseek(fp, 0, SEEK_END); + long fpsize = ftell(fp); + fseek(fp, 0, SEEK_SET); + char *file = malloc(fpsize + 1); + fread(file, 1, fpsize, fp); + char *lines[nc], *token; + int i = 0; + token = strtok(file, "\n"); + while (token != NULL) { + lines[i] = token; + token = strtok(NULL, "\n"); + i++; + } + while (!exit) { + /* build the pages */ + generate_files(lines[wc]); + wc--; + if (wc == -1) { + exit = 1; + } + } + fclose(fp); + free(file); + return 0; +} diff --git a/config.h b/config.h @@ -0,0 +1,13 @@ +/* do you want an rss file to be generated or not? */ +int genrss = 1; +/* what to append every html file with */ +char *appendhtml = "</article>\n</body\n</html>"; +/* what template to use for all html pages */ +char *htmltemplate = "template.html"; +/* url of website, important for rss */ +/* probably change this value */ +char *url = "https://kqueue.dev/"; +/* used for rss */ +/* you should probably change these values */ +char *rsstitle = "website"; +char *rssdesc = "website generated by adonis-ssg"; diff --git a/examples/README.md b/examples/README.md @@ -0,0 +1,5 @@ +run +``` +adonis-ssg conf +``` +to compile these pages diff --git a/examples/conf b/examples/conf @@ -0,0 +1,2 @@ +./example.md this is a example page ./example.html +./example2.md this is another example page ./example2.html diff --git a/examples/example.md b/examples/example.md @@ -0,0 +1,5 @@ +# example page +* bullet point +* asdf +* *adonis* +## the better static site generator diff --git a/examples/example2.md b/examples/example2.md @@ -0,0 +1,2 @@ +# this is another example page +**the better static site generator** diff --git a/examples/template.html b/examples/template.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<html lang="en"> +<meta charset="utf-8"> +<meta name="viewport" content="width=device-width, initial-scale=1"> +<link rel="icon" type="image/png" href="data:image/png;base64,"> +<title>INSERT-TITLE</title> +<style> + @media (prefers-color-scheme: dark){ + body {color:#fff;background:#000} + a:link {color:#9cf} + a:hover, a:visited:hover {color:#cef} + a:visited {color:#c9f} + } + body{ + margin:1em auto; + max-width:40em; + padding:0 .62em; + font:1.2em/1.62 sans-serif; + } + h1,h2,h3 { + line-height:1.2; + } + @media print{ + body{ + max-width:none + } + } +</style> +<article> diff --git a/libsmu.c b/libsmu.c @@ -0,0 +1,683 @@ +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +/* + * This is a modified version of smu to work with adonis-ssg + * (c) 2007-2014 Enno Boland <g s01 de> (c) 2020-2023 kqueue <kqueue@cocaine.ninja> + * Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. + + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING + * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#define LENGTH(x) sizeof(x)/sizeof(x[0]) +#define ADDC(b,i) if(i % BUFSIZ == 0) { b = realloc(b, (i + BUFSIZ) * sizeof(char)); if(!b) eprint("Malloc failed."); } b[i] +FILE *output; +typedef int (*Parser) (const char *, const char *, int); +typedef struct { + char *search; + int process; + char *before, *after; +} Tag; + +static int doamp(const char *begin, const char *end, int newblock); /* Parser for & */ +static int docomment(const char *begin, const char *end, int newblock); /* Parser for + * html-comments */ +static int dogtlt(const char *begin, const char *end, int newblock); /* Parser for < and > */ +static int dohtml(const char *begin, const char *end, int newblock); /* Parser for html */ +static int dolineprefix(const char *begin, const char *end, int newblock); /* Parser for line + * prefix tags */ +static int dolink(const char *begin, const char *end, int newblock); /* Parser for links and + * images */ +static int dolist(const char *begin, const char *end, int newblock); /* Parser for lists */ +static int doparagraph(const char *begin, const char *end, int newblock); /* Parser for paragraphs */ +static int doreplace(const char *begin, const char *end, int newblock); /* Parser for simple + * replaces */ +static int doshortlink(const char *begin, const char *end, int newblock); /* Parser for links and + * images */ +static int dosurround(const char *begin, const char *end, int newblock); /* Parser for + * surrounding tags */ +static int dounderline(const char *begin, const char *end, int newblock); /* Parser for underline + * tags */ +static void *ereallocz(void *p, size_t size); +static void hprint(const char *begin, const char *end); /* escapes HTML and + * prints it to output */ +static void process(const char *begin, const char *end, int isblock); /* Processes range + * between begin and + * end. */ + +/* list of parsers */ +static Parser parsers[] = {dounderline, docomment, dolineprefix, + dolist, doparagraph, dogtlt, dosurround, dolink, +doshortlink, dohtml, doamp, doreplace}; +static int nohtml = 0; + +static Tag lineprefix[] = { + {" ", 0, "<pre><code>", "\n</code></pre>"}, + {"\t", 0, "<pre><code>", "\n</code></pre>"}, + {">", 2, "<blockquote>", "</blockquote>"}, + {"###### ", 1, "<h6>", "</h6>"}, + {"##### ", 1, "<h5>", "</h5>"}, + {"#### ", 1, "<h4>", "</h4>"}, + {"### ", 1, "<h3>", "</h3>"}, + {"## ", 1, "<h2>", "</h2>"}, + {"# ", 1, "<h1>", "</h1>"}, + {"- - -\n", 1, "<hr />", ""}, +}; + +static Tag underline[] = { + {"=", 1, "<h1>", "</h1>\n"}, + {"-", 1, "<h2>", "</h2>\n"}, +}; + +static Tag surround[] = { + {"``", 0, "<code>", "</code>"}, + {"`", 0, "<code>", "</code>"}, + {"___", 1, "<strong><em>", "</em></strong>"}, + {"***", 1, "<strong><em>", "</em></strong>"}, + {"__", 1, "<strong>", "</strong>"}, + {"**", 1, "<strong>", "</strong>"}, + {"_", 1, "<em>", "</em>"}, + {"*", 1, "<em>", "</em>"}, +}; + +static const char *replace[][2] = { + {"\\\\", "\\"}, + {"\\`", "`"}, + {"\\*", "*"}, + {"\\_", "_"}, + {"\\{", "{"}, + {"\\}", "}"}, + {"\\[", "["}, + {"\\]", "]"}, + {"\\(", "("}, + {"\\)", ")"}, + {"\\#", "#"}, + {"\\+", "+"}, + {"\\-", "-"}, + {"\\.", "."}, + {"\\!", "!"}, +}; + +static const char *insert[][2] = { + {" \n", "<br />"}, +}; + +void +eprint(const char *format,...) +{ + va_list ap; + + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + exit(EXIT_FAILURE); +} + +int +doamp(const char *begin, const char *end, int newblock) +{ + const char *p; + + if (*begin != '&') + return 0; + if (!nohtml) { + for (p = begin + 1; p != end && !strchr("; \\\n\t", *p); p++); + if (p == end || *p == ';') + return 0; + } + fputs("&amp;", output); + return 1; +} + +int +dogtlt(const char *begin, const char *end, int newblock) +{ + int brpos; + char c; + + if (nohtml || begin + 1 >= end) + return 0; + brpos = begin[1] == '>'; + if (!brpos && *begin != '<') + return 0; + c = begin[brpos ? 0 : 1]; + if (!brpos && (c < 'a' || c > 'z') && (c < 'A' || c > 'Z')) { + fputs("&lt;", output); + return 1; + } else if (brpos && (c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && !strchr("/\"'", c)) { + fprintf(output, "%c&gt;", c); + return 2; + } + return 0; +} + +int +docomment(const char *begin, const char *end, int newblock) +{ + char *p; + + if (nohtml || strncmp("<!--", begin, 4)) + return 0; + p = strstr(begin, "-->"); + if (!p || p + 3 >= end) + return 0; + fprintf(output, "%.*s\n", (int) (p + 3 - begin), begin); + return (p + 3 - begin) * (newblock ? -1 : 1); +} + +int +dohtml(const char *begin, const char *end, int newblock) +{ + const char *p, *tag, *tagend; + + if (nohtml || begin + 2 >= end) + return 0; + p = begin; + if (p[0] != '<' || !isalpha(p[1])) + return 0; + p++; + tag = p; + for (; isalnum(*p) && p < end; p++); + tagend = p; + if (p > end || tag == tagend) + return 0; + while ((p = strstr(p, "</")) && p < end) { + p += 2; + if (strncmp(p, tag, tagend - tag) == 0 && p[tagend - tag] == '>') { + p++; + fwrite(begin, sizeof(char), p - begin + tagend - tag + 1, output); + return p - begin + tagend - tag + 1; + } + } + p = strchr(tagend, '>'); + if (p) { + fwrite(begin, sizeof(char), p - begin + 2, output); + return p - begin + 2; + } else + return 0; +} + +int +dolineprefix(const char *begin, const char *end, int newblock) +{ + unsigned int i, j, l; + char *buffer; + const char *p; + + if (newblock) + p = begin; + else if (*begin == '\n') + p = begin + 1; + else + return 0; + for (i = 0; i < LENGTH(lineprefix); i++) { + l = strlen(lineprefix[i].search); + if (end - p < l) + continue; + if (strncmp(lineprefix[i].search, p, l)) + continue; + if (*begin == '\n') + fputc('\n', output); + fputs(lineprefix[i].before, output); + if (lineprefix[i].search[l - 1] == '\n') { + fputc('\n', output); + return l - 1; + } + if (!(buffer = malloc(BUFSIZ))) + eprint("Malloc failed."); + buffer[0] = '\0'; + + /* Collect lines into buffer while they start with the prefix */ + j = 0; + while ((strncmp(lineprefix[i].search, p, l) == 0) && p + l < end) { + p += l; + + /* + * Special case for blockquotes: optional space after + * > + */ + if (lineprefix[i].search[0] == '>' && *p == ' ') { + p++; + } + while (p < end) { + ADDC(buffer, j) = *p; + j++; + if (*(p++) == '\n') + break; + } + } + + /* Skip empty lines in block */ + while (*(buffer + j - 1) == '\n') { + j--; + } + + ADDC(buffer, j) = '\0'; + if (lineprefix[i].process) + process(buffer, buffer + strlen(buffer), lineprefix[i].process >= 2); + else + hprint(buffer, buffer + strlen(buffer)); + fputs(lineprefix[i].after, output); + fputc('\n', output); + free(buffer); + return -(p - begin); + } + return 0; +} + +int +dolink(const char *begin, const char *end, int newblock) +{ + int img , len, sep, parens_depth = 1; + const char *desc, *link, *p, *q, *descend, *linkend; + const char *title = NULL, *titleend = NULL; + + if (*begin == '[') + img = 0; + else if (strncmp(begin, "![", 2) == 0) + img = 1; + else + return 0; + p = desc = begin + 1 + img; + if (!(p = strstr(desc, "](")) || p > end) + return 0; + for (q = strstr(desc, "!["); q && q < end && q < p; q = strstr(q + 1, "![")) + if (!(p = strstr(p + 1, "](")) || p > end) + return 0; + descend = p; + link = p + 2; + + /* find end of link while handling nested parens */ + q = link; + while (parens_depth) { + if (!(q = strpbrk(q, "()")) || q > end) + return 0; + if (*q == '(') + parens_depth++; + else + parens_depth--; + if (parens_depth && q < end) + q++; + } + + if ((p = strpbrk(link, "\"'")) && p < end && q > p) { + sep = p[0]; /* separator: can be " or ' */ + title = p + 1; + /* strip trailing whitespace */ + for (linkend = p; linkend > link && isspace(*(linkend - 1)); linkend--); + for (titleend = q - 1; titleend > link && isspace(*(titleend)); titleend--); + if (*titleend != sep) { + return 0; + } + } else { + linkend = q; + } + + /* Links can be given in angular brackets */ + if (*link == '<' && *(linkend - 1) == '>') { + link++; + linkend--; + } + len = q + 1 - begin; + if (img) { + fputs("<img src=\"", output); + hprint(link, linkend); + fputs("\" alt=\"", output); + hprint(desc, descend); + fputs("\" ", output); + if (title && titleend) { + fputs("title=\"", output); + hprint(title, titleend); + fputs("\" ", output); + } + fputs("/>", output); + } else { + fputs("<a href=\"", output); + hprint(link, linkend); + fputs("\"", output); + if (title && titleend) { + fputs(" title=\"", output); + hprint(title, titleend); + fputs("\"", output); + } + fputs(">", output); + process(desc, descend, 0); + fputs("</a>", output); + } + return len; +} + +int +dolist(const char *begin, const char *end, int newblock) +{ + unsigned int i, j, indent, run, ul, isblock; + const char *p, *q; + char *buffer = NULL; + char marker; + + isblock = 0; + if (newblock) + p = begin; + else if (*begin == '\n') + p = begin + 1; + else + return 0; + q = p; + if (*p == '-' || *p == '*' || *p == '+') { + ul = 1; + marker = *p; + } else { + ul = 0; + for (; p < end && *p >= '0' && *p <= '9'; p++); + if (p >= end || *p != '.') + return 0; + } + p++; + if (p >= end || !(*p == ' ' || *p == '\t')) + return 0; + for (p++; p != end && (*p == ' ' || *p == '\t'); p++); + indent = p - q; + buffer = ereallocz(buffer, BUFSIZ); + if (!newblock) + fputc('\n', output); + fputs(ul ? "<ul>\n" : "<ol>\n", output); + run = 1; + for (; p < end && run; p++) { + for (i = 0; p < end && run; p++, i++) { + if (*p == '\n') { + if (p + 1 == end) + break; + else { + /* Handle empty lines */ + for (q = p + 1; (*q == ' ' || *q == '\t') && q < end; q++); + if (*q == '\n') { + ADDC(buffer, i) = '\n'; + i++; + run = 0; + isblock++; + p = q; + } + } + q = p + 1; + j = 0; + if (ul && *q == marker) + j = 1; + else if (!ul) { + for (; q + j != end && q[j] >= '0' && q[j] <= '9' && j < indent; j++); + if (q + j == end) + break; + if (j > 0 && q[j] == '.') + j++; + else + j = 0; + } + if (q + indent < end) + for (; (q[j] == ' ' || q[j] == '\t') && j < indent; j++); + if (j == indent) { + ADDC(buffer, i) = '\n'; + i++; + p += indent; + run = 1; + if (*q == ' ' || *q == '\t') + p++; + else + break; + } else if (j < indent) + run = 0; + } + ADDC(buffer, i) = *p; + } + ADDC(buffer, i) = '\0'; + fputs("<li>", output); + process(buffer, buffer + i, isblock > 1 || (isblock == 1 && run)); + fputs("</li>\n", output); + } + fputs(ul ? "</ul>\n" : "</ol>\n", output); + free(buffer); + p--; + while (*(--p) == '\n'); + return -(p - begin + 1); +} + +int +doparagraph(const char *begin, const char *end, int newblock) +{ + const char *p; + + if (!newblock) + return 0; + p = strstr(begin, "\n\n"); + if (!p || p > end) + p = end; + if (p - begin <= 1) + return 0; + fputs("<p>", output); + process(begin, p, 0); + fputs("</p>\n", output); + return -(p - begin); +} + +int +doreplace(const char *begin, const char *end, int newblock) +{ + unsigned int i, l; + + for (i = 0; i < LENGTH(insert); i++) + if (strncmp(insert[i][0], begin, strlen(insert[i][0])) == 0) + fputs(insert[i][1], output); + for (i = 0; i < LENGTH(replace); i++) { + l = strlen(replace[i][0]); + if (end - begin < l) + continue; + if (strncmp(replace[i][0], begin, l) == 0) { + fputs(replace[i][1], output); + return l; + } + } + return 0; +} + +int +doshortlink(const char *begin, const char *end, int newblock) +{ + const char *p, *c; + int ismail = 0; + + if (*begin != '<') + return 0; + for (p = begin + 1; p != end; p++) { + switch (*p) { + case ' ': + case '\t': + case '\n': + return 0; + case '#': + case ':': + ismail = -1; + break; + case '@': + if (ismail == 0) + ismail = 1; + break; + case '>': + if (ismail == 0) + return 0; + fputs("<a href=\"", output); + if (ismail == 1) { + /* mailto: */ + fputs("&#x6D;&#x61;i&#x6C;&#x74;&#x6F;:", output); + for (c = begin + 1; *c != '>'; c++) + fprintf(output, "&#%u;", *c); + fputs("\">", output); + for (c = begin + 1; *c != '>'; c++) + fprintf(output, "&#%u;", *c); + } else { + hprint(begin + 1, p); + fputs("\">", output); + hprint(begin + 1, p); + } + fputs("</a>", output); + return p - begin + 1; + } + } + return 0; +} + +int +dosurround(const char *begin, const char *end, int newblock) +{ + unsigned int i, l; + const char *p, *start, *stop; + + for (i = 0; i < LENGTH(surround); i++) { + l = strlen(surround[i].search); + if (end - begin < 2 * l || strncmp(begin, surround[i].search, l) != 0) + continue; + start = begin + l; + p = start - 1; + do { + stop = p; + p = strstr(p + 1, surround[i].search); + } while (p && p[-1] == '\\'); + if (p && p[-1] != '\\') + stop = p; + if (!stop || stop < start || stop >= end) + continue; + fputs(surround[i].before, output); + + /* Single space at start and end are ignored */ + if (*start == ' ' && *(stop - 1) == ' ') { + start++; + stop--; + l++; + } + if (surround[i].process) + process(start, stop, 0); + else + hprint(start, stop); + fputs(surround[i].after, output); + return stop - begin + l; + } + return 0; +} + +int +dounderline(const char *begin, const char *end, int newblock) +{ + unsigned int i, j, l; + const char *p; + + if (!newblock) + return 0; + p = begin; + for (l = 0; p + l != end && p[l] != '\n'; l++); + p += l + 1; + if (l == 0) + return 0; + for (i = 0; i < LENGTH(underline); i++) { + for (j = 0; p + j != end && p[j] != '\n' && p[j] == underline[i].search[0]; j++); + if (j >= l) { + fputs(underline[i].before, output); + if (underline[i].process) + process(begin, begin + l, 0); + else + hprint(begin, begin + l); + fputs(underline[i].after, output); + return -(j + p - begin); + } + } + return 0; +} + +void * +ereallocz(void *p, size_t size) +{ + void *res; + if (p) + res = realloc(p, size); + else + res = calloc(1, size); + + if (!res) + eprint("fatal: could not malloc() %u bytes\n", size); + return res; +} + +void +hprint(const char *begin, const char *end) +{ + const char *p; + + for (p = begin; p != end; p++) { + if (*p == '&') + fputs("&amp;", output); + else if (*p == '"') + fputs("&quot;", output); + else if (*p == '>') + fputs("&gt;", output); + else if (*p == '<') + fputs("&lt;", output); + else + fputc(*p, output); + } +} + +void +process(const char *begin, const char *end, int newblock) +{ + const char *p, *q; + int affected; + unsigned int i; + + for (p = begin; p < end;) { + if (newblock) + while (*p == '\n') + if (++p == end) + return; + affected = 0; + for (i = 0; i < LENGTH(parsers) && !affected; i++) + affected = parsers[i] (p, end, newblock); + p += abs(affected); + if (!affected) { + if (nohtml) + hprint(p, p + 1); + else + fputc(*p, output); + p++; + } + for (q = p; q != end && *q == '\n'; q++); + if (q == end) + return; + else if (p[0] == '\n' && p + 1 != end && p[1] == '\n') + newblock = 1; + else + newblock = affected < 0; + } +} + +int +smu_convert(FILE * out, FILE * source, int supresshtml) +{ + char *buffer = NULL; + int s , i; + unsigned long len, bsize; + output = out; + nohtml = supresshtml; + bsize = 2 * BUFSIZ; + buffer = ereallocz(buffer, bsize); + len = 0; + while ((s = fread(buffer + len, 1, BUFSIZ, source))) { + len += s; + if (BUFSIZ + len + 1 > bsize) { + bsize += BUFSIZ; + if (!(buffer = realloc(buffer, bsize))) + eprint("realloc failed."); + } + } + buffer[len] = '\0'; + process(buffer, buffer + len, 1); + free(buffer); + return EXIT_SUCCESS; +} diff --git a/mkfile b/mkfile @@ -0,0 +1,5 @@ +all: + pcc adonis-ssg.c libsmu.c -o adonis-ssg +clean: + rm -f *.6 + rm -f adonis-ssg