srohtml/srohtml.c

1643 lines
33 KiB
C

#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 16
#define DATE_FORMAT "%d/%m/%Y"
#define DATE_LEN 10
#define FAVICON "data:image/gif;base64,R0lGODlhEAAQAKEAAAAAcP///wAAcAAAcCH5BAEKAAIALAAAAAAQABAAQAIwlBWZxxwAQWjtTQRvlZhTnGSfYT3eZ3WaE4mjGa1UasoH7c6tzYanfqmhJMKXglcAADs="
#define FIGCAPTION_TAG_LEN 12
#define FIGURE_SIZES_START 4
#define INDEX_START 16
#define LANG_LEN 2
#define LINE_MAX 1000
#define LINE_START 80
#define NOTES_START 8
#define NOTES_MAX_DIGITS 3
#define TIME_FORMAT "%H:%M"
typedef struct {
void *data;
size_t length;
size_t size;
size_t unit_size;
} array;
typedef struct {
int indentation;
char *name;
} closure;
typedef enum {
tag_content = 0,
tag_attribute = 1
} html_val_type;
typedef struct {
char *id;
char *print_width;
char *width;
} img;
typedef struct {
char *desc;
char *id;
char heading;
} index_title;
typedef struct {
char *desc;
char *href;
} note;
/* function declarations */
static void add_closure(closure *tag, const char *name, const int ind);
static void add_h_to_index(const char heading, const char *id);
static int add_note(const char *href, const char *desc);
static void *array_at(array *a, const size_t i);
static void array_init(array *a, const size_t size, size_t num);
static void *array_pop(array *a);
static void *array_push(array *a);
static char *array_strcat(array *dst, const char *src);
static char *array_strcat_html(array *dst, const char *src, const html_val_type t);
static void cmd(char *line);
static void cmd_a(const char *line);
static void cmd_au_de_ti(char **dst, const char *line);
static void cmd_br(void);
static void cmd_close(void);
static void cmd_default_figure_sizes(const char *line);
static void cmd_em(const char *line);
static void cmd_figure(const char *line);
static void cmd_h(const char *line);
static void cmd_hr(void);
static void cmd_i(const char *line);
static void cmd_index(const char *line);
static void cmd_lang(const char *line);
static void cmd_line_tag(const char *tag);
static void cmd_note(const char *line);
static void cmd_publication_date(const char *line);
static void cmd_q(const char *line);
static void cmd_strong(const char *line);
static void cmd_tag_end(const char *tag);
static void cmd_tag_start(const char *tag, const char *id, const char *class);
static void cmd_time(const char *line);
static void cmd_url(const char *line);
static void findent(const int ind, FILE *f);
static void footer(void);
static void footnotes(void);
static int fputs_html(const char *str, FILE *f, const html_val_type t);
static size_t fread_line(FILE *file, char **str, size_t *len);
static void indent_str(char **str, const int ind);
static void nav_index(FILE *f);
static void outputs(FILE *out);
static void page_head(void);
static void pop_closure(array *a);
static void print_help(void);
static void print_usage(void);
static void print_version(void);
static void read_and_process(FILE *in);
static void *safe_malloc(const size_t size);
static void *safe_realloc(void *ptr, const size_t size);
static void srohtml(FILE *in, FILE *out);
static size_t stread_param(const char *src, char **str);
/* variables */
char *author = NULL;
array body;
char *description = NULL;
int figcaption_presence = 0;
array figure_sizes;
array head;
size_t heading_position = 0;
int img_presence = 0;
int indentation = 2;
array index;
char *index_heading = NULL;
char *index_heading_id = NULL;
size_t index_position = 0;
char *language = NULL;
int li_presence = 0;
int licence_container = 0;
int nav_presence = 0;
char *publication_date = NULL;
array sitography;
int space = 0;
char tag_closure[CLS_MAX];
char *title = NULL;
/* function implementations */
void
add_closure(closure *tag, const char *name, const int ind)
{
tag->indentation = ind;
tag->name = safe_malloc(sizeof(char) * (strlen(name) + 1));
strcpy(tag->name, name);
}
void
add_h_to_index(const char heading, const char *id)
{
index_title *new;
new = array_push(&index);
new->desc = NULL;
new->heading = heading;
new->id = safe_malloc(sizeof(char) * (strlen(id) + 1));
strcpy(new->id, id);
heading_position = strlen(body.data);
}
int
add_note(const char *href, const char *desc)
{
note *new;
new = array_push(&sitography);
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);
return sitography.length;
}
void *
array_at(array *a, const size_t i)
{
if (i >= a->length)
return NULL;
return (char *) a->data + i * a->unit_size;
}
void
array_init(array *a, const size_t size, size_t num)
{
num = num == 0 ? 1 : num;
a->data = safe_malloc(size * num);
a->length = 0;
a->size = num;
a->unit_size = size;
}
void *
array_pop(array *a)
{
if (a->length == 0)
return NULL;
a->length--;
return (char *) a->data + a->length * a->unit_size;
}
void *
array_push(array *a)
{
if (a->length == a->size) {
a->size *= 2;
a->data = safe_realloc(a->data, a->unit_size * a->size);
}
return (char *) a->data + a->length++ * a->unit_size;
}
char *
array_strcat(array *dst, const char *src)
{
char *c;
size_t i, len;
len = strlen(src);
for (i = 0; i < len; i++) {
c = array_push(dst);
*c = src[i];
}
c = array_push(dst);
*c = '\0';
dst->length--;
return dst->data;
}
char *
array_strcat_html(array *dst, const char *src, const html_val_type t)
{
char *c;
size_t i, len;
len = strlen(src);
for (i = 0; i < len; i++) {
if (t == tag_attribute && src[i] == '"')
array_strcat(dst, "&quot;");
else if (src[i] == '&')
array_strcat(dst, "&amp;");
else if (src[i] == '<')
array_strcat(dst, "&lt;");
else if (src[i] == '>')
array_strcat(dst, "&gt;");
else {
c = array_push(dst);
*c = src[i];
}
}
c = array_push(dst);
*c = '\0';
dst->length--;
return dst->data;
}
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, "AUTHOR", cmdlen))
cmd_au_de_ti(&author, line);
else if (!strncmp(line, "BR", cmdlen))
cmd_br();
else if (!strncmp(line, "DEFAULT_FIGURE_SIZES", cmdlen))
cmd_default_figure_sizes(line);
else if (!strncmp(line, "DESCRIPTION", cmdlen))
cmd_au_de_ti(&description, line);
else if (!strncmp(line, "TIME", cmdlen))
cmd_time(line);
else if (!strncmp(line, "EM", cmdlen))
cmd_em(line);
else if (!strncmp(line, "FIGURE", cmdlen))
cmd_figure(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, "I", cmdlen))
cmd_i(line);
else if (!strncmp(line, "INDEX", cmdlen))
cmd_index(line);
else if (!strncmp(line, "LANG", cmdlen))
cmd_lang(line);
else if (!strncmp(line, "LI", cmdlen)) {
cmd_line_tag("li");
li_presence = 1;
} else if (!strncmp(line, "LICENSE_END", cmdlen))
cmd_tag_end("div");
else if (!strncmp(line, "LICENSE_START", cmdlen)) {
cmd_tag_start("div", "", "contenitore-licenza");
cmd_hr();
} else if (!strncmp(line, "NAV_END", cmdlen))
cmd_tag_end("nav");
else if (!strncmp(line, "NAV_START", cmdlen)) {
cmd_tag_start("nav", "", "");
nav_presence = 1;
} else if (!strncmp(line, "NOTE", cmdlen))
cmd_note(line);
else if (!strncmp(line, "P", cmdlen))
cmd_line_tag("p");
else if (!strncmp(line, "PUBLICATION_DATE", cmdlen))
cmd_publication_date(line);
else if (!strncmp(line, "Q", cmdlen))
cmd_q(line);
else if (!strncmp(line, "STRONG", cmdlen))
cmd_strong(line);
else if (!strncmp(line, "TITLE", cmdlen))
cmd_au_de_ti(&title, line);
else if (!strncmp(line, "UL_END", cmdlen))
cmd_tag_end("ul");
else if (!strncmp(line, "UL_START", cmdlen))
cmd_tag_start("ul", "", "");
else if (!strncmp(line, "URL", cmdlen))
cmd_url(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)
array_strcat(&body, " ");
array_strcat(&body, "<a class=\"");
if (!strcmp(href, in))
array_strcat(&body, "url");
else
array_strcat(&body, "nav");
array_strcat(&body, "\" href=\"");
array_strcat(&body, href);
array_strcat(&body, "\"");
if (strcmp(title, "")) {
array_strcat(&body, " title=\"");
array_strcat_html(&body, title, tag_attribute);
array_strcat(&body, "\"");
}
array_strcat(&body, ">");
array_strcat_html(&body, in, tag_attribute);
array_strcat(&body, "</a>");
free(href);
free(in);
free(title);
}
void
cmd_au_de_ti(char **dst, const char *line)
{
/* move past command */
line = strchr(line, ' ');
if (line == NULL)
return;
/* move past ' ', if present */
if (line[0] == ' ')
line++;
if (*dst != NULL)
free(*dst);
*dst = safe_malloc(sizeof(char) * (strlen(line) + 1));
strcpy(*dst, line);
}
void
cmd_br(void)
{
array_strcat(&body, "<br>");
space = 0;
}
void
cmd_close(void)
{
char *bodystr;
char *ind, *line;
index_title *title;
bodystr = body.data;
ind = NULL;
line = NULL;
title = NULL;
if (!strcmp(tag_closure, "</figure>")) {
if (body.length >= FIGCAPTION_TAG_LEN + indentation)
line = bodystr + body.length - FIGCAPTION_TAG_LEN;
if (line != NULL && !strcmp(line, "<figcaption>")) {
*(line - indentation) = '\0';
body.length = strlen(bodystr);
} else {
figcaption_presence = 1;
array_strcat(&body, "</figcaption>\n");
}
indent_str(&ind, --indentation);
array_strcat(&body, ind);
free(ind);
}
if ((!strcmp(tag_closure, "</h3>") ||
!strcmp(tag_closure, "</h4>") ||
!strcmp(tag_closure, "</h5>") ||
!strcmp(tag_closure, "</h6>")) &&
index_position && heading_position) {
title = array_at(&index, index.length - 1);
if (title == NULL) {
fprintf(stderr, BIN ": Can't find title at position %i, in index array.\n", index.length - 1);
exit(1);
}
title->desc = safe_malloc(sizeof(char) * (strlen(bodystr + heading_position) + 1));
strcpy(title->desc, bodystr + heading_position);
heading_position = 0;
}
array_strcat(&body, tag_closure);
array_strcat(&body, "\n");
strcpy(tag_closure, "");
space = 0;
}
void
cmd_default_figure_sizes(const char *line)
{
char *print_width, *width;
img *sizes;
print_width = NULL;
width = NULL;
/* move past "DEFAULT_FIGURE_SIZES" */
line += 20;
/* move past arguments, once found them */
line += stread_param(line, &width);
line += stread_param(line, &print_width);
if (strcmp(width, "")) {
sizes = array_at(&figure_sizes, 0);
if (sizes == NULL)
sizes = array_push(&figure_sizes);
sizes->id = NULL;
sizes->print_width = NULL;
sizes->width = width;
if (strcmp(print_width, ""))
sizes->print_width = print_width;
}
if (!strcmp(print_width, ""))
free(print_width);
if (!strcmp(width, ""))
free(width);
}
void
cmd_em(const char *line)
{
/* move past "EM" */
line += 2;
/* move past ' ', if present */
if (line[0] == ' ')
line++;
if (space)
array_strcat(&body, " ");
array_strcat(&body, "<em>");
array_strcat(&body, line);
array_strcat(&body, "</em>");
}
void
cmd_figure(const char *line)
{
char *id, *ind, *print_width, *src, *width;
img *sizes;
id = NULL;
ind = NULL;
print_width = NULL;
src = NULL;
width = NULL;
/* move past "FIGURE" */
line += 6;
/* move past arguments, once found them */
line += stread_param(line, &src);
line += stread_param(line, &id);
line += stread_param(line, &width);
line += stread_param(line, &print_width);
indent_str(&ind, indentation);
array_strcat(&body, ind);
array_strcat(&body, "<figure>\n");
indent_str(&ind, ++indentation);
array_strcat(&body, ind);
array_strcat(&body, "<img");
if (strcmp(id, "")) {
array_strcat(&body, " id=\"");
array_strcat_html(&body, id, tag_attribute);
array_strcat(&body, "\"");
}
array_strcat(&body, " src=\"");
array_strcat_html(&body, src, tag_attribute);
array_strcat(&body, "\" alt=\"\">\n");
img_presence = 1;
array_strcat(&body, ind);
array_strcat(&body, "<figcaption>");
strcpy(tag_closure, "</figure>");
if (strcmp(id, "") && strcmp(width, "")) {
sizes = array_push(&figure_sizes);
sizes->id = id;
sizes->print_width = NULL;
sizes->width = width;
if (strcmp(print_width, ""))
sizes->print_width = print_width;
}
if (!strcmp(width, ""))
free(id);
free(ind);
if (!strcmp(print_width, ""))
free(print_width);
free(src);
if (!strcmp(width, ""))
free(width);
}
void
cmd_h(const char *line)
{
const char *id;
char h[2], *ind;
h[0] = line[1];
h[1] = '\0';
id = NULL;
ind = NULL;
indent_str(&ind, indentation);
array_strcat(&body, ind);
array_strcat(&body, "<h");
array_strcat(&body, h);
if (strlen(line) > 4 && line[2] == ' ' && line[3] != '\0') {
id = line + 3;
array_strcat(&body, " id=\"");
array_strcat_html(&body, id, tag_attribute);
array_strcat(&body, "\"");
}
array_strcat(&body, ">");
strcpy(tag_closure, "</h");
tag_closure[3] = line[1];
tag_closure[4] = '>';
tag_closure[5] = '\0';
if (id != NULL && index_position != 0)
add_h_to_index(line[1], id);
free(ind);
}
void
cmd_hr(void)
{
char *ind;
ind = NULL;
indent_str(&ind, indentation);
array_strcat(&body, ind);
array_strcat(&body, "<hr>\n");
free(ind);
}
void
cmd_i(const char *line)
{
/* move past "I" */
line += 1;
/* move past ' ', if present */
if (line[0] == ' ')
line++;
if (space)
array_strcat(&body, " ");
array_strcat(&body, "<i>");
array_strcat(&body, line);
array_strcat(&body, "</i>");
space = 1;
}
void
cmd_index(const char *line)
{
/* move past "INDEX" */
line += 5;
/* move past arguments, once found them */
line += stread_param(line, &index_heading);
line += stread_param(line, &index_heading_id);
if (!strcmp(index_heading, "")) {
free(index_heading);
index_heading = NULL;
}
if (!strcmp(index_heading_id, "")) {
free(index_heading_id);
index_heading_id = NULL;
}
index_position = body.length;
}
void
cmd_lang(const char *line)
{
/* move past "LANG" */
line += 4;
/* move past ' ', if present */
if (line[0] == ' ')
line++;
if (strlen(line) != LANG_LEN)
return;
if (language == NULL) {
language = safe_malloc(sizeof(char) * (LANG_LEN + 1));
language[LANG_LEN] = '\0';
}
strncpy(language, line, LANG_LEN);
}
void
cmd_line_tag(const char *tag)
{
char *ind;
ind = NULL;
indent_str(&ind, indentation);
array_strcat(&body, ind);
array_strcat(&body, "<");
array_strcat(&body, tag);
array_strcat(&body, ">");
strcpy(tag_closure, "</");
strcat(tag_closure, tag);
strcat(tag_closure, ">");
free(ind);
}
void
cmd_nav_end(const char *line)
{
char *ind;
ind = NULL;
indentation--;
indent_str(&ind, indentation);
array_strcat(&body, ind);
array_strcat(&body, "</nav>\n");
free(ind);
}
void
cmd_note(const char *line)
{
char *desc, *href, *title;
char index[NOTES_MAX_DIGITS + 1];
desc = NULL;
href = NULL;
title = NULL;
/* move past "NOTE" */
line += 4;
/* move past arguments, once found them */
line += stread_param(line, &href);
line += stread_param(line, &desc);
line += stread_param(line, &title);
if (space)
array_strcat(&body, " ");
sprintf(index, "%u", add_note(href, desc));
array_strcat(&body, "<sup><a id=\"nota-");
array_strcat(&body, index);
array_strcat(&body, "\" class=\"nav\" href=\"#sitografia-");
array_strcat(&body, index);
array_strcat(&body, "\"");
if (strcmp(title, "")) {
array_strcat(&body, " title=\"");
array_strcat_html(&body, title, tag_attribute);
array_strcat(&body, "\"");
}
array_strcat(&body, ">[");
array_strcat(&body, index);
array_strcat(&body, "]</a></sup>");
free(desc);
free(href);
free(title);
}
void
cmd_publication_date(const char *line)
{
/* move past "PUBLICATION_DATE" */
line += 16;
/* move past ' ', if present */
if (line[0] == ' ')
line++;
if (strlen(line) != DATE_LEN)
return;
if (publication_date == NULL) {
publication_date = safe_malloc(sizeof(char) * (DATE_LEN + 1));
publication_date[DATE_LEN] = '\0';
}
strncpy(publication_date, line, DATE_LEN);
}
void
cmd_q(const char *line)
{
char *cite;
cite = NULL;
/* move past "Q" */
line += 1;
/* move past argument, if found it */
line += stread_param(line, &cite);
if (space)
array_strcat(&body, " ");
array_strcat(&body, "<q");
if (strcmp(cite, "")) {
array_strcat(&body, " cite=\"");
array_strcat_html(&body, cite, tag_attribute);
array_strcat(&body, "\"");
}
array_strcat(&body, ">");
space = 0;
strcpy(tag_closure, "</q></p>");
free(cite);
}
void
cmd_strong(const char *line)
{
char *bodystr;
bodystr = body.data;
/* move past "STRONG" */
line += 6;
/* move past space, if present */
if (line[0] == ' ')
line++;
if (space && body.length > 0 && bodystr[body.length - 1] != '\'')
array_strcat(&body, " ");
array_strcat(&body, "<strong>");
array_strcat(&body, line);
array_strcat(&body, "</strong>");
}
void
cmd_tag_end(const char *tag)
{
char *ind;
ind = NULL;
indentation--;
indent_str(&ind, indentation);
array_strcat(&body, ind);
array_strcat(&body, "</");
array_strcat(&body, tag);
array_strcat(&body, ">\n");
free(ind);
}
void
cmd_tag_start(const char *tag, const char *id, const char *class)
{
char *ind;
ind = NULL;
licence_container = 1;
indent_str(&ind, indentation);
array_strcat(&body, ind);
array_strcat(&body, "<");
array_strcat(&body, tag);
if (strcmp(id, "")) {
array_strcat(&body, " id=\"");
array_strcat_html(&body, id, tag_attribute);
array_strcat(&body, "\"");
}
if (strcmp(class, "")) {
array_strcat(&body, " class=\"");
array_strcat_html(&body, class, tag_attribute);
array_strcat(&body, "\"");
}
array_strcat(&body, ">\n");
indentation++;
free(ind);
}
void
cmd_time(const char *line)
{
char *datetime, *in;
datetime = NULL;
in = NULL;
/* move past "TIME" */
line += 4;
/* move past arguments, once found them */
line += stread_param(line, &datetime);
line += stread_param(line, &in);
if (space)
array_strcat(&body, " ");
array_strcat(&body, "<time datetime=\"");
array_strcat_html(&body, datetime, tag_attribute);
array_strcat(&body, "\">");
if (!strcmp(in, ""))
array_strcat_html(&body, datetime, tag_content);
else
array_strcat_html(&body, in, tag_content);
array_strcat(&body, "</time>");
free(datetime);
free(in);
}
void
cmd_url(const char *line)
{
/* move past "URL" */
line += 3;
/* move past ' ', if present */
if (line[0] == ' ')
line++;
if (space)
array_strcat(&body, " ");
array_strcat(&body, "&lt;<a class=\"url\" href=\"");
array_strcat_html(&body, line, tag_attribute);
array_strcat(&body, "\">");
array_strcat_html(&body, line, tag_content);
array_strcat(&body, "</a>&gt;");
}
void
findent(const int ind, FILE *f)
{
char *str;
str = NULL;
indent_str(&str, ind);
fputs(str, f);
free(str);
}
void
footer(void)
{
size_t max;
struct tm *now;
char str[22];
time_t s;
int tm_mday, tm_mon, tm_year;
struct tm tm_pd = { 0 };
max = 22;
array_strcat(&body, "\t\t<footer>\n");
if (publication_date != NULL) {
array_strcat(&body, "\t\t\t<p>Pubblicazione: <time datetime=\"");
array_strcat(&body, publication_date);
array_strcat(&body, "\">");
sscanf(publication_date, "%4u-%2u-%2u", &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, DATE_FORMAT, &tm_pd);
array_strcat(&body, str);
array_strcat(&body, "</time></p>\n");
free(publication_date);
}
s = time(NULL);
now = localtime(&s);
array_strcat(&body, "\t\t\t<p>Revisione: <time datetime=\"");
strftime(str, max, "%Y-%m-%dT%H:%M%z", now);
array_strcat(&body, str);
array_strcat(&body, "\">");
strftime(str, max, DATE_FORMAT " " TIME_FORMAT, now);
array_strcat_html(&body, str, tag_content);
array_strcat(&body, "</time></p>\n");
array_strcat(&body, "\t\t</footer>\n");
}
void
footnotes(void)
{
note *cur;
int i;
size_t dig_num, x;
char id[NOTES_MAX_DIGITS + 1];
char pad[6 * (NOTES_MAX_DIGITS - 1) + 1];
if (sitography.length == 0)
return;
array_strcat(&body, "\t\t<hr>\n");
array_strcat(&body, "\t\t<div class=\"contenitore-sitografia\">\n");
array_strcat(&body, "\t\t\t<h3 id=\"sitografia\">Sitografia</h3>\n");
cur = sitography.data;
sprintf(id, "%u", sitography.length);
dig_num = strlen(id);
for (i = 0; i < sitography.length; i++) {
sprintf(id, "%u", i + 1);
array_strcat(&body, "\t\t\t<p id=\"sitografia-");
array_strcat(&body, id);
array_strcat(&body, "\"><span>");
strcpy(pad, "");
for (x = strlen(id); x < dig_num; x++)
strcat(pad, "&nbsp;");
array_strcat(&body, pad);
array_strcat(&body, "<a class=\"nav\" href=\"#nota-");
array_strcat(&body, id);
array_strcat(&body, "\">");
array_strcat(&body, id);
array_strcat(&body, "</a></span> ");
if (strcmp(cur->desc, "")) {
array_strcat_html(&body, cur->desc, tag_content);
array_strcat(&body, " ");
}
array_strcat(&body, "&lt;<a class=\"url\" href=\"");
array_strcat(&body, cur->href);
array_strcat(&body, "\">");
array_strcat_html(&body, cur->href, tag_content);
array_strcat(&body, "</a>&gt;</p>\n");
free(cur->desc);
free(cur->href);
cur++;
}
array_strcat(&body, "\t\t</div>\n");
}
int
fputs_html(const char *str, FILE *f, const html_val_type t)
{
size_t i, len;
int res;
len = strlen(str);
res = 0;
for (i = 0; i < len; i++) {
if (res == EOF)
return EOF;
if (t == tag_attribute && str[i] == '"')
res = fputs("&quot;", f);
else if (str[i] == '&')
res = fputs("&amp;", f);
else if (str[i] == '<')
res = fputs("&lt;", f);
else if (str[i] == '>')
res = fputs("&gt;", f);
else
res = fputc(str[i], f);
}
return 1;
}
size_t
fread_line(FILE *file, char **str, size_t *len)
{
int c;
size_t i;
if (*str == NULL) {
*len = LINE_START + 1;
*str = safe_malloc(sizeof(char) * *len);
}
i = 0;
while ((c = fgetc(file)) && c != '\n' && c != EOF && i < LINE_MAX) {
if (i >= *len - 1) {
*len *= 2;
*str = safe_realloc(*str, sizeof(char) * *len);
}
(*str)[i] = c;
i++;
}
(*str)[i] = '\0';
*len = i;
return c == EOF ? EOF : i;
}
void
indent_str(char **str, const int ind)
{
int i;
if (*str == NULL || strlen(*str) <= ind) {
free(*str);
*str = safe_malloc(sizeof(char) * (ind + 1));
}
for (i = 0; i < ind; i++)
(*str)[i] = '\t';
(*str)[ind] = '\0';
}
int
main(int argc, char *argv[])
{
FILE *in, *out;
in = NULL;
out = NULL;
if (argc == 2) {
if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
print_help();
if (!strcmp(argv[1], "-v") || !strcmp(argv[1], "--version"))
print_version();
}
if (argc > 3) {
print_usage();
return 1;
}
if (argc >= 2)
in = fopen(argv[1], "r");
else
in = stdin;
if (in == NULL) {
fprintf(stderr, BIN ": Can't open %s.\n", argv[1]);
return 1;
}
if (argc == 3)
out = fopen(argv[2], "w");
else
out = stdout;
if (out == NULL) {
fprintf(stderr, BIN ": Can't open %s.\n", argv[2]);
return 1;
}
srohtml(in, out);
fclose(in);
fclose(out);
return 0;
}
void
nav_index(FILE *f)
{
int i, n;
int ind;
closure *tag;
array nav_cls;
index_title *next_title, *title;
ind = 2;
array_init(&nav_cls, sizeof(closure), 4);
tag = NULL;
findent(ind++, f);
fputs("<nav>\n", f);
findent(ind, f);
fputs("<hr>\n", f);
if (index_heading != NULL) {
findent(ind, f);
fputs("<h3", f);
if (index_heading_id != NULL) {
fputs(" id=\"", f);
fputs_html(index_heading_id, f, tag_attribute);
fputs("\"", f);
free(index_heading_id);
}
fputs(">", f);
fputs_html(index_heading, f, tag_content);
fputs("</h3>\n", f);
free(index_heading);
}
title = index.data;
for (i = 0; i < index.length; i++) {
next_title = i == index.length - 1 ? NULL : title + 1;
if (i == 0) {
findent(ind, f);
fputs("<ul>\n", f);
add_closure(array_push(&nav_cls), "ul", ind);
ind++;
}
findent(ind, f);
fputs("<li><a href=\"#", f);
fputs_html(title->id, f, tag_attribute);
fprintf(f, "\">%s</a>", title->desc);
add_closure(array_push(&nav_cls), "li", ind);
if (i == index.length - 1) {
fputs("</li>\n", f);
ind--;
pop_closure(&nav_cls);
n = nav_cls.length - 1;
for (n = nav_cls.length - 1; n < nav_cls.length; n--) {
tag = (closure *) nav_cls.data + n;
findent(tag->indentation, f);
fprintf(f, "</%s>\n", tag->name);
free(tag->name);
if (n == 0)
break;
}
findent(tag->indentation, f);
fputs("<hr>\n", f);
ind--;
} else if (next_title->heading < title->heading) {
fputs("</li>\n", f);
pop_closure(&nav_cls);
findent(--ind, f);
fputs("</ul>\n", f);
pop_closure(&nav_cls);
findent(--ind, f);
fputs("</li>\n", f);
pop_closure(&nav_cls);
} else if (next_title->heading > title->heading) {
fputs("\n", f);
findent(++ind, f);
fputs("<ul>\n", f);
add_closure(array_push(&nav_cls), "ul", ind);
ind++;
} else if ((title + 1)->heading == title->heading) {
fputs("</li>\n", f);
pop_closure(&nav_cls);
}
free(title->desc);
free(title->id);
title++;
}
findent(--tag->indentation, f);
fputs("</nav>\n", f);
free(nav_cls.data);
}
void
outputs(FILE *out)
{
char *bodystr;
char c;
bodystr = body.data;
c = bodystr[index_position];
fputs(head.data, out);
bodystr[index_position] = '\0';
fputs(bodystr, out);
if (index_position)
nav_index(out);
bodystr[index_position] = c;
fputs(bodystr + index_position, out);
}
void
page_head(void)
{
size_t i;
img *sizes;
array_strcat(&head, "<!doctype html>\n");
array_strcat(&head, "<html");
if (language != NULL) {
array_strcat(&head, " lang=\"");
array_strcat_html(&head, language, tag_attribute);
array_strcat(&head, "\"");
free(language);
}
array_strcat(&head, ">\n");
array_strcat(&head, "\t<head>\n");
array_strcat(&head, "\t\t<link rel=\"icon\" href=\"" FAVICON "\">\n");
array_strcat(&head, "\t\t<meta charset=\"utf-8\">\n");
if (author != NULL) {
array_strcat(&head, "\t\t<meta name=\"author\" content=\"");
array_strcat_html(&head, author, tag_attribute);
array_strcat(&head, "\">\n");
free(author);
}
if (description != NULL) {
array_strcat(&head, "\t\t<meta name=\"description\" content=\"");
array_strcat_html(&head, description, tag_attribute);
array_strcat(&head, "\">\n");
free(description);
}
array_strcat(&head, "\t\t<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n");
array_strcat(&head, "\t\t<style>\n" \
" body {\n" \
" margin: .5em auto;\n" \
" max-width: 50em;\n" \
" width: 90%;\n" \
" }\n" \
"\n" \
" p"
);
if (index_position || li_presence)
array_strcat(&head, ",\n\t\t\tli");
if (figcaption_presence)
array_strcat(&head, ",\n\t\t\tfigcaption");
array_strcat(&head, " {");
array_strcat(&head, "\n" \
" line-height: 1.5;\n" \
" }\n" \
"\n" \
" a.url {\n" \
" hyphens: manual;\n" \
" word-wrap: break-word;\n" \
" }\n"
);
if (img_presence) {
array_strcat(&head, "\n" \
" figure {\n" \
" margin-left: 0;\n" \
" margin-right: 0;\n" \
" text-align: center;\n" \
" }\n" \
"\n" \
" img {\n" \
" max-width: 100%;\n"
);
if (figure_sizes.length > 0) {
array_strcat(&head, \
" width: " \
);
sizes = array_at(&figure_sizes, 0);
array_strcat(&head, sizes->width);
array_strcat(&head, ";\n");
} else {
array_strcat(&head, \
" width: 7em;\n" \
);
}
array_strcat(&head, \
" }\n" \
);
for (i = 1; i < figure_sizes.length; i++) {
sizes = array_at(&figure_sizes, i);
array_strcat(&head, "\n" \
" img#" \
);
array_strcat(&head, sizes->id);
array_strcat(&head, " {\n" \
" width: " \
);
array_strcat(&head, sizes->width);
array_strcat(&head, ";\n" \
" }\n" \
);
}
}
if (figcaption_presence) {
array_strcat(&head, "\n" \
" figcaption {\n" \
" font-style: italic;\n" \
" }\n" \
);
}
if (licence_container) {
array_strcat(&head, "\n" \
" .contenitore-licenza {\n" \
" font-size: .8em;\n" \
" text-align: justify;\n" \
" }\n"
);
}
if (sitography.length > 0) {
array_strcat(&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"
);
}
array_strcat(&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"
);
if (index_position || nav_presence) {
array_strcat(&head, "\n" \
" nav {\n" \
" display: none;\n" \
" }\n"
);
}
array_strcat(&head, "\n" \
" a.nav {\n" \
" color: inherit;\n" \
" text-decoration: none;\n" \
" }\n"
);
for (i = 0; i < figure_sizes.length; i++) {
sizes = array_at(&figure_sizes, i);
if (sizes->print_width) {
array_strcat(&head, "\n" \
" img" \
);
if (i != 0) {
array_strcat(&head, "#");
array_strcat(&head, sizes->id);
}
array_strcat(&head, " {\n" \
" width: " \
);
array_strcat(&head, sizes->print_width);
array_strcat(&head, ";\n" \
" }\n" \
);
}
free(sizes->id);
free(sizes->print_width);
free(sizes->width);
}
array_strcat(&head, \
" }\n"
);
array_strcat(&head, "\t\t</style>\n");
if (title != NULL) {
array_strcat(&head, "\t\t<title>");
array_strcat_html(&head, title, tag_content);
array_strcat(&head, "</title>\n");
free(title);
}
array_strcat(&head, "\t</head>\n");
}
void
pop_closure(array *a)
{
closure *tag;
if (a->length <= 0)
return;
tag = array_at(a, a->length - 1);
if (tag == NULL) {
fprintf(stderr, BIN ": Can't find tag closure at position %i, in array type.\n", a->length - 1);
exit(1);
}
free(tag->name);
array_pop(a);
}
void
print_help(void)
{
print_usage();
fputs("\n", stdout);
fputs("Without INPUT, the roff source code will be read from standard input.\n", stdout);
fputs("Without OUTPUT, the resulting HTML will be sent to standard output.\n", stdout);
fputs("To use a different output from stdout, you must specify the input first,\n", stdout);
fputs("or use shell redirection.\n", stdout);
fputs("\n", stdout);
fputs("\"" BIN " -h\" or \"--help\" prints this help text.\n", stdout);
fputs("\"" BIN " -v\" or \"--version\" prints version and license information.\n", stdout);
fputs("\n", stdout);
fputs(BIN " exits with 0 only when it converts the input into HTML,\n", stdout);
fputs("otherwise it exits with 1.\n", stdout);
exit(1);
}
void
print_usage(void)
{
fputs("Usage: " BIN " [INPUT [OUTPUT]]\n", stdout);
}
void
print_version(void)
{
fputs(BIN " " VERSION "\n", stdout);
fputs("Copyright (C) 2024 Matteo Bini\n", stdout);
fputs("License: GPLv3+ <http://www.gnu.org/licenses/gpl.html>.\n", stdout);
fputs("This is free software: you are free to change and redistribute it.\n", stdout);
fputs("There is NO WARRANTY, to the extent permitted by law.\n", stdout);
fputs("\n", stdout);
fputs("Written by Matteo Bini.\n", stdout);
exit(1);
}
void
read_and_process(FILE *in)
{
size_t len;
char *line;
len = 0;
line = NULL;
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] != ';' &&
line[0] != '?')
array_strcat(&body, " ");
array_strcat_html(&body, line, tag_content);
space = 1;
}
free(line);
len = 0;
line = NULL;
}
free(line);
if (strcmp(tag_closure, ""))
cmd_close();
}
void *
safe_malloc(const size_t size)
{
void *ptr;
ptr = malloc(size);
if (ptr == NULL) {
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) {
fprintf(stderr, BIN ": Can't allocate %lu bytes.\n", size);
exit(1);
}
return new;
}
void
srohtml(FILE *in, FILE *out)
{
array_init(&head, sizeof(char), BODY_START_SIZE / 4);
array_init(&body, sizeof(char), BODY_START_SIZE);
array_init(&figure_sizes, sizeof(img), FIGURE_SIZES_START);
array_init(&index, sizeof(index_title), INDEX_START);
array_init(&sitography, sizeof(note), NOTES_START);
array_strcat(&body, "\t<body>\n");
read_and_process(in);
page_head();
footnotes();
footer();
array_strcat(&body, "\t</body>\n</html>\n");
outputs(out);
free(head.data);
free(body.data);
free(figure_sizes.data);
free(index.data);
free(sitography.data);
}
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;
}