srohtml/srohtml.c

914 lines
18 KiB
C
Raw Normal View History

#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
/* macros */
#define BODY_START_SIZE 26624 /* 26 x 1024 byte = 26 KiB */
#define CLS_MAX 8
#define DATE_LEN 10
#define FAVICON "data:image/gif;base64,R0lGODlhEAAQAKEAAAAAcP///wAAcAAAcCH5BAEKAAIALAAAAAAQABAAQAIwlBWZxxwAQWjtTQRvlZhTnGSfYT3eZ3WaE4mjGa1UasoH7c6tzYanfqmhJMKXglcAADs="
#define LANG_LEN 2
#define LINE_MAX 1000
#define NOTES_START 8
#define NOTES_MAX_DIGITS 3
typedef struct {
char *desc;
char *href;
} note;
typedef struct {
size_t index;
size_t size;
note *notes;
} references;
typedef struct {
size_t len;
size_t size;
char *str;
} string;
/* function declarations */
static size_t add_note(const char *href, const char *desc);
static void cmd(char *line);
static void cmd_close(void);
static void cmd_a(const char *line);
static void cmd_au_de_ti(char **str, const char *line);
static void cmd_dt(const char *line);
static void cmd_h(const char *line);
static void cmd_hr(void);
static void cmd_la(const char *line);
static void cmd_le(void);
static void cmd_ls(void);
static void cmd_no(const char *line);
static void cmd_p(void);
static void cmd_pd(const char *line);
static void cmd_st(const char *line);
static void cmd_tm(const char *line);
static void cmd_ur(const char *line);
static void footer(void);
static void footnotes(void);
static int fread_line(FILE *file, char **str, size_t *len);
static void indent_str(char **str);
static void page_head(void);
static void *safe_malloc(const size_t size);
static void *safe_realloc(void *ptr, const size_t size);
static size_t stread_param(const char *src, char **str);
static char *string_cat(string *dst, const char *src);
static char *string_cat_html(string *dst, const char *src, const int is_attr_val);
static void string_enlarge(string *old, const size_t size);
static int string_putc(string *dst, const char c);
/* variables */
char *author = NULL;
char closure[CLS_MAX];
char *description = NULL;
int indentation = 2;
char language[LANG_LEN + 1] = "";
int licence_container = 0;
int space = 0;
string head;
string body;
char pub_date[DATE_LEN + 1] = "";
references sitography;
char *title = NULL;
/* function implementations */
size_t
add_note(const char *href, const char *desc)
{
note *new;
if (sitography.index == sitography.size) {
sitography.size *= 2;
sitography.notes = safe_realloc(sitography.notes, sizeof(note) * sitography.size);
}
new = sitography.notes + sitography.index;
new->href = safe_malloc(sizeof(char) * (strlen(href) + 1));
strcpy(new->href, href);
new->desc = safe_malloc(sizeof(char) * (strlen(desc) + 1));
strcpy(new->desc, desc);
sitography.index++;
return sitography.index;
}
void
cmd(char *line)
{
char *end;
size_t cmdlen;
if (!(end = strchr(line, ' ')))
end = line + strlen(line);
cmdlen = end - ++line;
if (!strncmp(line, "A", cmdlen))
cmd_a(line);
else if (!strncmp(line, "AU", cmdlen))
cmd_au_de_ti(&author, line);
else if (!strncmp(line, "DE", cmdlen))
cmd_au_de_ti(&description, line);
else if (!strncmp(line, "DT", cmdlen))
cmd_dt(line);
else if (!strncmp(line, "H1", cmdlen))
cmd_h(line);
else if (!strncmp(line, "H2", cmdlen))
cmd_h(line);
else if (!strncmp(line, "H3", cmdlen))
cmd_h(line);
else if (!strncmp(line, "H4", cmdlen))
cmd_h(line);
else if (!strncmp(line, "H5", cmdlen))
cmd_h(line);
else if (!strncmp(line, "H6", cmdlen))
cmd_h(line);
else if (!strncmp(line, "HR", cmdlen))
cmd_hr();
else if (!strncmp(line, "LA", cmdlen))
cmd_la(line);
else if (!strncmp(line, "LE", cmdlen))
cmd_le();
else if (!strncmp(line, "LS", cmdlen))
cmd_ls();
else if (!strncmp(line, "NO", cmdlen))
cmd_no(line);
else if (!strncmp(line, "P", cmdlen))
cmd_p();
else if (!strncmp(line, "PD", cmdlen))
cmd_pd(line);
else if (!strncmp(line, "ST", cmdlen))
cmd_st(line);
else if (!strncmp(line, "TI", cmdlen))
cmd_au_de_ti(&title, line);
else if (!strncmp(line, "TM", cmdlen))
cmd_tm(line);
else if (!strncmp(line, "UR", cmdlen))
cmd_ur(line);
}
void
cmd_a(const char *line)
{
char *href, *in, *title;
href = NULL;
in = NULL;
title = NULL;
/* move past "A" */
line += 1;
/* move past arguments, once found them */
line += stread_param(line, &href);
line += stread_param(line, &in);
line += stread_param(line, &title);
if (space)
string_cat(&body, " ");
string_cat(&body, "<a class=\"");
if (!strcmp(href, in))
string_cat(&body, "url");
else
string_cat(&body, "nav");
string_cat(&body, "\" href=\"");
string_cat(&body, href);
string_cat(&body, "\"");
if (strcmp(title, "")) {
string_cat(&body, " title=\"");
string_cat_html(&body, title, 1);
string_cat(&body, "\"");
}
string_cat(&body, ">");
string_cat_html(&body, in, 1);
string_cat(&body, "</a>");
free(href);
free(in);
free(title);
}
void
cmd_au_de_ti(char **str, const char *line)
{
/* move past command */
line += 2;
/* move past ' ', if present */
if (line[0] == ' ')
line++;
if (*str != NULL)
free(*str);
*str = safe_malloc(sizeof(char) * (strlen(line) + 1));
strcpy(*str, line);
}
void
cmd_close(void)
{
string_cat(&body, closure);
string_cat(&body, "\n");
strcpy(closure, "");
space = 0;
}
void
cmd_dt(const char *line)
{
char *datetime, *in;
datetime = NULL;
in = NULL;
/* move past "DT" */
line += 2;
/* move past arguments, once found them */
line += stread_param(line, &datetime);
line += stread_param(line, &in);
if (space)
string_cat(&body, " ");
string_cat(&body, "<time datetime=\"");
string_cat_html(&body, datetime, 1);
string_cat(&body, "\">");
if (!strcmp(in, ""))
string_cat_html(&body, datetime, 0);
else
string_cat_html(&body, in, 0);
string_cat(&body, "</time>");
free(datetime);
free(in);
}
void
cmd_h(const char *line)
{
string_cat(&body, "\t\t<h");
string_putc(&body, line[1]);
if (line[2] == ' ' && line[3] != '\0') {
string_cat(&body, " id=\"");
string_cat_html(&body, line + 3, 1);
string_cat(&body, "\"");
}
string_cat(&body, ">");
strcpy(closure, "</h");
closure[3] = line[1];
closure[4] = '>';
closure[5] = '\0';
}
void
cmd_hr(void)
{
string_cat(&body, "\t\t<hr>");
}
void
cmd_la(const char *line)
{
/* move past "LA" */
line += 2;
/* move past ' ', if present */
if (line[0] == ' ')
line++;
if (strlen(line) != LANG_LEN)
return;
strncpy(language, line, LANG_LEN);
}
void
cmd_le(void)
{
char *ind;
ind = NULL;
indentation--;
indent_str(&ind);
string_cat(&body, ind);
string_cat(&body, "</div>\n");
free(ind);
}
void
cmd_ls(void)
{
char *ind;
ind = NULL;
licence_container = 1;
indent_str(&ind);
string_cat(&body, ind);
string_cat(&body, "<div class=\"contenitore-licenza\">\n");
indentation++;
free(ind);
ind = NULL;
indent_str(&ind);
string_cat(&body, ind);
string_cat(&body, "<hr>\n");
free(ind);
}
void
cmd_no(const char *line)
{
char *desc, *href, *title;
char index[NOTES_MAX_DIGITS + 1];
desc = NULL;
href = NULL;
title = NULL;
/* move past "NO" */
line += 2;
/* move past arguments, once found them */
line += stread_param(line, &href);
line += stread_param(line, &desc);
line += stread_param(line, &title);
if (space)
string_cat(&body, " ");
sprintf(index, "%u", add_note(href, desc));
string_cat(&body, "<sup><a id=\"nota-");
string_cat(&body, index);
string_cat(&body, "\" class=\"nav\" href=\"#sitografia-");
string_cat(&body, index);
string_cat(&body, "\"");
if (strcmp(title, "")) {
string_cat(&body, " title=\"");
string_cat_html(&body, title, 1);
string_cat(&body, "\"");
}
string_cat(&body, ">[");
string_cat(&body, index);
string_cat(&body, "]</a></sup>");
free(desc);
free(href);
free(title);
}
void
cmd_p(void)
{
char *ind;
ind = NULL;
indent_str(&ind);
string_cat(&body, ind);
string_cat(&body, "<p>");
strcpy(closure, "</p>");
free(ind);
}
void
cmd_pd(const char *line)
{
size_t len;
/* move past "PD" */
line += 2;
len = strlen(line);
/* move past ' ' after PD */
if (len && line[0] == ' ') {
line++;
len--;
if (len == DATE_LEN)
strncpy(pub_date, line, DATE_LEN);
}
}
void
cmd_st(const char *line)
{
/* move past "ST" */
line += 2;
/* move past space, if present */
if (line[0] == ' ')
line++;
if (space)
string_cat(&body, " ");
string_cat(&body, "<strong>");
string_cat(&body, line);
string_cat(&body, "</strong>");
}
void
cmd_tm(const char *line)
{
char *date, *hours, *in;
/* move past "TM" */
line += 2;
/* move past arguments, once found them */
line += stread_param(line, &date);
line += stread_param(line, &hours);
line += stread_param(line, &in);
if (space)
string_cat(&body, " ");
string_cat(&body, "<time datetime=\"");
string_cat(&body, date);
if (strcmp(hours, "")) {
string_cat(&body, "T");
string_cat(&body, hours);
}
string_cat(&body, "\">");
string_cat_html(&body, in, 0);
string_cat(&body, "</time>");
free(date);
free(hours);
free(in);
}
void
cmd_ur(const char *line)
{
/* move past "UR" */
line += 2;
/* move past ' ', if present */
if (line[0] == ' ')
line++;
if (space)
string_cat(&body, " ");
string_cat(&body, "&lt;<a class=\"url\" href=\"");
string_cat_html(&body, line, 1);
string_cat(&body, "\">");
string_cat_html(&body, line, 0);
string_cat(&body, "</a>&gt;");
}
void
footer(void)
{
size_t max;
struct tm *now;
char str[16];
time_t s;
int tm_mday, tm_mon, tm_year;
struct tm tm_pd = { 0 };
max = 16;
string_cat(&body, "\t\t<footer>\n");
if (strcmp(pub_date, "")) {
string_cat(&body, "\t\t\t<p>Pubblicazione: <time datetime=\"");
string_cat(&body, pub_date);
string_cat(&body, "\">");
sscanf(pub_date, "%4i-%2i-%2i", &tm_year, &tm_mon, &tm_mday);
tm_pd.tm_mday = tm_mday;
tm_pd.tm_mon = --tm_mon;
tm_pd.tm_year = tm_year - 1900;
strftime(str, max, "%x", &tm_pd);
string_cat(&body, str);
string_cat(&body, "</time></p>\n");
}
s = time(NULL);
now = localtime(&s);
string_cat(&body, "\t\t\t<p>Revisione: <time datetime=\"");
strftime(str, max, "%Y-%m-%d", now);
string_cat(&body, str);
strftime(str, max, "%R", now);
string_cat(&body, "T");
string_cat(&body, str);
strftime(str, max, "%z", now);
string_cat(&body, str);
string_cat(&body, "\">");
strftime(str, max, "%x", now);
string_cat_html(&body, str, 0);
strftime(str, max, " %R", now);
string_cat_html(&body, str, 0);
string_cat(&body, "</time></p>\n");
string_cat(&body, "\t\t</footer>\n");
}
void
footnotes(void)
{
note *cur;
size_t i, dig_num, x;
char id[NOTES_MAX_DIGITS + 1];
char pad[6 * (NOTES_MAX_DIGITS - 1) + 1];
if (sitography.index == 0)
return;
string_cat(&body, "\t\t<hr>\n");
string_cat(&body, "\t\t<div class=\"contenitore-sitografia\">\n");
string_cat(&body, "\t\t\t<h3 id=\"sitografia\">Sitografia</h3>\n");
sprintf(id, "%u", sitography.index);
dig_num = strlen(id);
for (i = 0; i < sitography.index; i++) {
cur = sitography.notes + i;
sprintf(id, "%u", i + 1);
string_cat(&body, "\t\t\t<p id=\"sitografia-");
string_cat(&body, id);
string_cat(&body, "\"><span>");
strcpy(pad, "");
for (x = strlen(id); x < dig_num; x++)
strcat(pad, "&nbsp;");
string_cat(&body, pad);
string_cat(&body, "<a class=\"nav\" href=\"#nota-");
string_cat(&body, id);
string_cat(&body, "\">");
string_cat(&body, id);
string_cat(&body, "</a></span> ");
if (strcmp(cur->desc, "")) {
string_cat_html(&body, cur->desc, 0);
string_cat(&body, " ");
}
string_cat(&body, "&lt;<a class=\"url\" href=\"");
string_cat(&body, cur->href);
string_cat(&body, "\">");
string_cat_html(&body, cur->href, 0);
string_cat(&body, "</a>&gt;</p>\n");
free(cur->desc);
free(cur->href);
}
free(sitography.notes);
string_cat(&body, "\t\t</div>\n");
}
int
fread_line(FILE *file, char **str, size_t *len)
{
int c;
char *end;
size_t i;
i = 0;
while ((c = fgetc(file)) && c != '\n' && c != EOF && i < LINE_MAX)
i++;
*str = safe_malloc(i + 1);
fseek(file, -i - 1, SEEK_CUR);
fread(*str, 1, i, file);
(*str)[i] = '\0';
*len = i;
fseek(file, 1, SEEK_CUR);
return c == EOF ? EOF : c;
}
void
indent_str(char **str)
{
int i;
*str = safe_malloc(sizeof(char) * (indentation + 1));
for (i = 0; i < indentation; i++)
(*str)[i] = '\t';
(*str)[indentation] = '\0';
}
int
main (int argc, char *argv[])
{
FILE *in, *out;
size_t len;
char *line;
2024-07-15 08:43:40 +00:00
if (argc == 2 && (!strcmp(argv[1], "-v") || !strcmp(argv[1], "--version"))) {
fprintf(stdout, BIN " " VERSION "\n");
fprintf(stdout, "Copyright (C) 2024 Matteo Bini\n");
fprintf(stdout, "License: GPLv3+ <http://www.gnu.org/licenses/gpl.html>.\n");
fprintf(stdout, "This is free software: you are free to change and redistribute it.\n");
fprintf(stdout, "There is NO WARRANTY, to the extent permitted by law.\n");
fprintf(stdout, "\n");
fprintf(stdout, "Written by Matteo Bini.\n");
return 1;
}
setlocale(LC_ALL, "");
in = fopen(argv[1], "r");
if (in == NULL) {
2024-07-15 08:40:34 +00:00
fprintf(stderr, BIN ": Can't open %s.\n", argv[1]);
return 1;
}
head.size = BODY_START_SIZE / 4;
head.str = safe_malloc(head.size);
strcpy(head.str, "");
head.len = 0;
body.size = BODY_START_SIZE;
body.str = safe_malloc(body.size);
strcpy(body.str, "");
body.len = 0;
string_cat(&body, "\t<body>\n");
sitography.index = 0;
sitography.size = NOTES_START;
sitography.notes = malloc(sizeof(note) * sitography.size);
while (fread_line(in, &line, &len) != EOF) {
if (!len)
cmd_close();
else if (len >= 2 && line[0] == '.')
cmd(line);
else if (len >= 2 && line[0] == '\\' && line[1] == '"')
continue;
else {
if (space &&
line[0] != '!' &&
line[0] != ',' &&
line[0] != '.' &&
line[0] != '.' &&
line[0] != ';' &&
line[0] != '?')
string_cat(&body, " ");
string_cat_html(&body, line, 0);
space = 1;
}
free(line);
}
free(line);
fclose(in);
if (strcmp(closure, ""))
cmd_close();
page_head();
footnotes();
footer();
string_cat(&body, "\t</body>\n</html>\n");
if (argc < 3)
out = stdout;
else
out = fopen(argv[2], "w");
if (out == NULL) {
2024-07-15 08:40:34 +00:00
fprintf(stderr, BIN ": Can't open %s.\n", argv[2]);
return 1;
}
fputs(head.str, out);
fputs(body.str, out);
fclose(out);
return 0;
}
void
page_head(void)
{
string_cat(&head, "<!doctype html>\n");
string_cat(&head, "<html lang=\"");
string_cat_html(&head, language, 1);
string_cat(&head, "\">\n");
string_cat(&head, "\t<head>\n");
string_cat(&head, "\t\t<link rel=\"icon\" href=\"" FAVICON "\">\n");
string_cat(&head, "\t\t<meta charset=\"utf-8\">\n");
string_cat(&head, "\t\t<meta name=\"author\" content=\"");
string_cat_html(&head, author, 1);
string_cat(&head, "\">\n");
string_cat(&head, "\t\t<meta name=\"description\" content=\"");
string_cat_html(&head, description, 1);
string_cat(&head, "\">\n");
string_cat(&head, "\t\t<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n");
string_cat(&head, "\t\t<style>\n" \
" body {\n" \
" margin: .5em auto;\n" \
" max-width: 50em;\n" \
" width: 90%;\n" \
" }\n" \
"\n" \
" p {\n" \
" line-height: 1.5;\n" \
" }\n" \
"\n" \
" a.url {\n" \
2024-07-15 07:58:28 +00:00
" hyphens: manual;\n" \
" word-wrap: break-word;\n" \
" }\n"
);
if (licence_container) {
string_cat(&head, "\n" \
" .contenitore-licenza {\n" \
" font-size: .8em;\n" \
" text-align: justify;\n" \
" }\n"
);
}
if (sitography.index > 0) {
string_cat(&head, "\n" \
" sup {\n" \
" font-size: .7em;\n" \
" }\n" \
"\n" \
" .contenitore-sitografia p {\n" \
" margin: .5em 0;\n" \
" text-align: left;\n" \
" }\n" \
"\n" \
" .contenitore-sitografia p span {\n" \
" font-family: monospace;\n" \
" }\n"
);
}
string_cat(&head, "\n" \
" footer {\n" \
" margin: 2em auto 1em;\n" \
" }\n" \
"\n" \
" footer p {\n" \
" margin: 0 auto;\n" \
" text-align: center;\n" \
" }\n" \
"\n" \
" @media all and (min-width: 50em) {\n" \
" p {\n" \
" hyphens: auto;\n" \
" text-align: justify;\n" \
" }\n" \
" }\n" \
"\n" \
" @media print {\n" \
" body {\n" \
" max-width: 90%;\n" \
" }\n" \
"\n" \
" a.nav {\n" \
" color: inherit;\n" \
" text-decoration: none;\n" \
" }\n" \
" }\n"
);
string_cat(&head, "\t\t</style>\n");
string_cat(&head, "\t\t<title>");
string_cat_html(&head, title, 0);
string_cat(&head, "</title>\n");
string_cat(&head, "\t</head>\n");
free(author);
free(description);
2024-07-13 18:45:52 +00:00
free(title);
}
void *
safe_malloc(const size_t size)
{
void *ptr;
ptr = malloc(size);
if (ptr == NULL) {
2024-07-15 08:40:34 +00:00
fprintf(stderr, BIN ": Can't allocate %lu bytes.\n", size);
exit(1);
}
return ptr;
}
void *
safe_realloc(void *ptr, const size_t size)
{
void *new;
new = realloc(ptr, size);
if (new == NULL) {
2024-07-15 08:40:34 +00:00
fprintf(stderr, BIN ": Can't allocate %lu bytes.\n", size);
exit(1);
}
return new;
}
size_t
stread_param(const char *src, char **str)
{
char c;
char *end;
size_t i, len;
int p;
c = ' ';
p = 0;
if (src[0] == ' ') {
src++;
p++;
}
if (src[0] == '"' || src[0] == '\'') {
c = src[0];
src++;
p += 2;
}
end = strchr(src, c);
if (end)
len = end - src;
else
len = strlen(src);
*str = safe_malloc(len + 1);
for (i = 0; i <= len; i++)
(*str)[i] = '\0';
strncpy(*str, src, len);
return len + p;
}
char *
string_cat(string *dst, const char *src)
{
size_t len;
len = strlen(src);
if (len >= (dst->size - dst->len))
string_enlarge(dst, (dst->size + len) * 2 + 1);
strcat(dst->str, src);
dst->len = dst->len + len;
return dst->str;
}
char *
string_cat_html(string *dst, const char *src, const int is_attr_val)
{
size_t i, len;
len = strlen(src);
for (i = 0; i < len; i++) {
if (is_attr_val && src[i] == '"')
string_cat(dst, "&quot;");
else if (src[i] == '&')
string_cat(dst, "&amp;");
else if (src[i] == '<')
string_cat(dst, "&lt;");
else if (src[i] == '>')
string_cat(dst, "&gt;");
else
string_putc(dst, src[i]);
}
return dst->str;
}
void
string_enlarge(string *old, const size_t size)
{
old->str = safe_realloc(old->str, sizeof(char) * size);
old->size = size;
}
int
string_putc(string *dst, const char c)
{
if ((dst->size - dst->len) <= 1)
string_enlarge(dst, (dst->size + 1) * 2 + 1);
dst->str[dst->len] = c;
dst->str[dst->len + 1] = '\0';
dst->len++;
return (int) c;
}