site/tools/metagen.c
2025-03-30 21:48:59 -04:00

145 lines
3.9 KiB
C

/* SPDX-License-Identifier: GPL-3.0-or-later */
/* compile with `clang metagen.c -std=c89 -I/usr/include/libxml2 -lxml2' */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libxml2/libxml/tree.h>
#include <libxml2/libxml/parser.h>
#define SMALL 512 /* 0.5KiB */
#define BIG 524288 /* 512KiB */
/*
This program generates a HTML list of user sites. It was made for Midgard, but
you can feel free to use it for your own projects and modify it to fit your
needs (following GNU GPLv3).
*/
void help(char program[]) {
fprintf(stderr, "Usage: %s /path/to/xml\n", program);
fprintf(stderr, "For example, %s /srv/http/users/*/site-meta.xml\n", program);
exit(EXIT_FAILURE);
}
char* get_child(const char *parent, const char *tag, xmlDocPtr doc, xmlNodePtr node) {
/* get the content of a nested element */
static char out[BIG];
xmlNodePtr child_node = NULL;
while (node != NULL) {
if (!xmlStrcmp(node->name, (const xmlChar *)parent)) {
child_node = node->children;
break;
} else {
node = node->next;
}
}
while (child_node != NULL) {
if (!xmlStrcmp(child_node->name, (const xmlChar *)tag)) {
sprintf(out, "%s", xmlNodeListGetString(doc, child_node->xmlChildrenNode, 1));
return out;
}
child_node = child_node->next;
}
return "NOT_FOUND";
}
char* get_tag(const char* tag, xmlDocPtr doc, xmlNodePtr node) {
/* get the content of an element */
static char out[BIG];
while (node != NULL) {
if (!xmlStrcmp(node->name, (const xmlChar *)tag)) {
sprintf(out, "%s", xmlNodeListGetString(doc, node->xmlChildrenNode, 1));
return out;
}
node = node->next;
}
return "NOT_FOUND";
}
const char* gen_html(const char* filename) {
/* This function is more close to being the actual main one,
it's where the HTML is generated.
What it generates _should_ be valid XHTML verifiable on https://validator.w3.org,
if you care about that. */
xmlDocPtr source = xmlReadFile(filename, NULL, 0);
xmlNodePtr cur = NULL;
static char out[BIG];
/* these just check if the file is really messed up */
if (source == NULL) {
fprintf(stderr, "failed to parse file\n");
xmlFreeDoc(source);
} else {
cur = xmlDocGetRootElement(source);
}
if (cur == NULL) {
fprintf(stderr, "empty\n");
xmlFreeDoc(source);
exit(EXIT_FAILURE);
}
if (xmlStrcmp(cur->name, (const xmlChar *) "site")) {
fprintf(stderr, "root tag must be <site>");
xmlFreeDoc(source);
exit(EXIT_FAILURE);
}
cur = cur->xmlChildrenNode;
/* these are for iterating over later */
char protocols[4][SMALL] = {"gemini", "gopher", "nex", "web"};
char networks[5][SMALL] = {"i2p_b32", "i2p_ah", "tor", "ygg", "alfis"};
/* you're supposed to put the output here into a <dl> */
/* it's messy, but it works (kind of) */
printf(" <dt><span class=\"site-title\">%s</span>", get_tag("title", source, cur));
printf(" <sub>%s</sub></dt>\n", get_tag("lang", source, cur));
printf(" <dd>\n");
printf(" <p>\n");
printf(" %s\n", get_tag("description", source, cur));
printf(" </p>\n");
const char *error = "NOT_FOUND"; /* whatever is in here, if a tag is equal to that it will be discarded */
printf(" <p>\n");
int i;
for (i = 0; i < (sizeof(protocols) / sizeof(protocols[0])); i++) {
char *tag = get_tag(protocols[i], source, cur);
if (tag != error) {
printf(" %s: ", protocols[i]);
int j = 0;
for (j = 0; j < (sizeof(networks) / sizeof(networks[0])); j++) {
char *tag = get_child(protocols[i], networks[j], source, cur);
if (!strcmp(networks[j], "alfis") && tag != error) {
printf("<a href=\"%s\">ygg (alfis)</a> ", tag);
} else if (tag != error) {
printf("<a href=\"%s\">%s</a> ", tag, networks[j]);
}
}
printf("\n");
}
}
printf(" </p>\n");
printf(" </dd>\n");
return out;
}
int main(int argc, char *argv[]) {
if (argc == 1) {
help(argv[0]);
}
int i;
for (i = 1; i < argc; i++) {
printf("%s", gen_html(argv[i]));
}
return EXIT_SUCCESS;
}