config parser (WIP)

This commit is contained in:
Your Name 2025-06-08 20:52:43 +03:00
parent d0f2593887
commit 77ade504f6
14 changed files with 810 additions and 632 deletions

20
config Normal file
View file

@ -0,0 +1,20 @@
# Absolute path to
ROOT www
# Html generator
CSS style.css
LOGO logo.png
DESC 8img
TITLE / img
POST_PER_PAGE 5
# RSS generator
LINK https://nlight.tilde.team/img/
XML_FILE index.xml
LANGUAGE en-US
MAX_POST_COUNT 20
# Date
DATE_FORMAT %a, %d %h %Y %H:%M:%S %z

View file

@ -1,23 +0,0 @@
/* Absolute path to */
#define ROOT "8img_dir"
#define DB ROOT"/db"
/* Html generator */
#define CSS "style.css"
#define LOGO "../files/logo.png"
#define DESC "cyberlake"
#define TITLE "/ img"
#define POST_PER_PAGE 5
/* RSS generator */
#define LINK "https://nlight.tilde.team/img/"
#define XML_FILE "index.xml"
#define LANGUAGE "en-US"
#define MAX_POST_COUNT 20
/* Date */
#define DATE_FORMAT "%a, %d %h %Y %H:%M:%S %z"

609
img.c
View file

@ -1,609 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <limits.h>
#include <sys/stat.h>
#include <time.h>
#include "config.h"
#define CHECK_URL(x) ( (sizeof(x) > 2 && x[sizeof(x) - 2] == '/') ? "" : "/" )
struct DB_STR {
char *filename;
char **tags;
size_t size;
char *desc;
time_t birthtime;
struct DB_STR *next;
};
struct UNIQ_TAGS {
char **tags;
size_t size;
};
char *create_path(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
static char path[PATH_MAX + 1];
vsnprintf(path, sizeof(path) - 1, fmt, args);
va_end(args);
return path;
}
char *gen_id(const char *filename) {
const char *alf = "qwertyuiopasdfghjklzxcvbnm1234567890QWERTYUIOPASDFGHJKLZXCVBNM";
char *ext = strrchr(filename, '.');
if (ext == NULL)
ext = "";
size_t size = 7 + strlen(ext);
char *id = malloc(size);
if (id == NULL)
return NULL;
for (size_t i = 0; i < size - strlen(ext); i++)
id[i] = alf[rand() % sizeof(alf)];
strcat(id, ext);
id[size] = '\0';
return id;
}
FILE *file_open(const char *file, const char *mode) {
FILE *fp = fopen(file, mode);
if (fp == NULL) {
fprintf(stderr, "8img: %s: %s\n", file, strerror(errno));
return NULL;
}
return fp;
}
void copy(const char *src, const char *dst) {
FILE *ifp = file_open(src, "r");
if (ifp == NULL)
exit(1);
FILE *ofp = file_open(dst, "w");
if (ofp == NULL) {
fclose(ifp);
exit(1);
}
char buf[4096];
size_t ret = 0;
while ((ret = fread(buf, sizeof(char), sizeof(buf), ifp)))
fwrite(buf, sizeof(char), ret, ofp);
fclose(ifp);
fclose(ofp);
}
void add(char **list, const int size, const char *desc) {
char *name = NULL;
char *new_path;
while (1) {
name = gen_id(list[0]);
if (name == NULL) {
fprintf(stderr, "8img: malloc: %s\n", strerror(errno));
exit(1);
}
new_path = create_path("%s/%s", ROOT, name);
struct stat sb;
if (stat(new_path, &sb) < 0)
break;
}
/* Copy file */
copy(list[0], new_path);
/* Update db */
FILE *fp = file_open(DB, "a");
if (fp == NULL) {
free(name);
exit(1);
}
/* Write "filename|tag1,tag2...|desc" to the db */
fprintf(fp, "%s|", name);
if (size > 1) {
for (int i = 1; i < size; i++) {
if (strchr(list[i], ',') == NULL || strchr(list[i], ' ') == NULL)
fprintf(fp, "%s%c", list[i], (i == size - 1) ? '|' : ',');
else
fprintf(stderr, "8img: tag '%s' contain ',' or ' '\n", list[i]);
}
}
else
fputs("no_context|", fp);
if (desc)
fputs(desc, fp);
fputc('\n', fp);
free(name);
fclose(fp);
}
void free_db(struct DB_STR *db) {
while (db != NULL) {
struct DB_STR *i = db->next;
if (db->filename != NULL)
free(db->filename);
if (db->desc != NULL)
free(db->desc);
if (db->tags != NULL) {
for (size_t i = 0; i < db->size; i++)
if (db->tags[i] != NULL)
free(db->tags[i]);
free(db->tags);
}
free(db);
db = i;
}
}
int append_tag(struct DB_STR *db, const char *tok, const size_t allocated) {
if (allocated == 0) {
db->tags = malloc(sizeof(char *) * (db->size + 1));
if (db->tags == NULL)
return 1;
db->tags[0] = strdup(tok);
if (db->tags[0] == NULL) {
free(db->tags);
return 1;
}
}
else {
db->tags[allocated] = strdup(tok);
if (db->tags[allocated] == NULL)
return 1;
}
return 0;
}
struct DB_STR *new_db_value(char *str) {
struct DB_STR *db = malloc(sizeof(struct DB_STR));
if (db == NULL)
return NULL;
memset(db, 0, sizeof(struct DB_STR));
char *ptr = strrchr(str, '\n');
if (ptr != NULL)
*ptr = '\0';
/* Filename */
char *tok_rest = str;
char *tok = strtok_r(tok_rest, "|", &tok_rest);
if (tok != NULL) {
db->filename = strdup(tok);
if (db->filename == NULL)
goto NEW_DB_VALUE_CLOSE;
}
else
goto NEW_DB_VALUE_CLOSE;
/* Tags */
tok = strtok_r(tok_rest, "|", &tok_rest);
if (tok != NULL) {
db->size = 1;
for (size_t i = 0; i < strlen(tok); i++)
if (tok[i] == ',' && tok[i + 1])
db->size++;
size_t allocated = 0;
char *tok2_rest = tok;
char *tok2;
while ((tok2 = strtok_r(tok2_rest, ",", &tok2_rest)) != NULL) {
if (append_tag(db, tok2, allocated))
goto NEW_DB_VALUE_CLOSE;
allocated++;
}
}
/* Desc */
tok = strtok_r(tok_rest, "|", &tok_rest);
if (tok != NULL)
db->desc = strdup(tok);
/* Stats */
char *new_path = create_path("%s/%s", ROOT, db->filename);
struct stat sb;
if (stat(new_path, &sb) < 0)
fprintf(stderr, "8img: stat: %s: %s\n", db->filename, strerror(errno));
db->birthtime = sb.st_mtime;
return db;
NEW_DB_VALUE_CLOSE:
free_db(db);
return NULL;
}
struct DB_STR *read_db(void) {
FILE *fp = file_open(DB, "r");
if (fp == NULL)
exit(1);
int ret = 1;
char *line = NULL;
ssize_t bytes = 0;
size_t n = 0;
struct DB_STR *db = NULL;
struct DB_STR *i = NULL;
while ((bytes = getline(&line, &n, fp)) > 0) {
db = new_db_value(line);
if (db == NULL)
goto READ_DB_CLOSE;
db->next = i;
i = db;
}
ret = 0;
READ_DB_CLOSE:
fclose(fp);
if (line)
free(line);
if (ret) {
free_db(db);
fprintf(stderr, "8img: db: %s\n", strerror(errno));
}
return db;
}
char *GetDate(time_t time) {
struct tm *tm = localtime(&time);
static char buf[256];
if (strftime(buf, sizeof(buf) - 1, DATE_FORMAT, tm) > 0)
return buf;
return "Unknown";
}
struct DB_STR *build_html_page(FILE *fp, const char *file, const struct UNIQ_TAGS ut, struct DB_STR *db, const size_t page, const size_t pages) {
fprintf(fp, "<html>\n<head>\n<link rel='stylesheet' href='%s'>\n<title>%s</title>\n<meta charset='UTF-8'>\n</head>\n<body>\n<center><a href='index.html'><img id='logo' src='%s'></a>\n<h1 id='desc'>%s | <a href='%s'>RSS</a><br>%s/p%zu</h1></center>\n", CSS, TITLE, LOGO, DESC, XML_FILE, file, page);
/* Tags */
fputs("<center><div class='alltags'>\n<a href='index.html'>all</a>", fp);
for (size_t i = 0; i < ut.size; i++)
fprintf(fp, "<a href='%s.html'> %s</a>", ut.tags[i], ut.tags[i]);
fputs("\n</center></div><br><br>\n", fp);
/* Images */
size_t images = 0;
struct DB_STR *p = db;
while (p != NULL) {
struct DB_STR *i = p->next;
if (strcmp(file, "index")) {
char flag = 1;
for (size_t i = 0; i < p->size; i++)
if (!strcmp(p->tags[i], file))
flag = 0;
if (flag) {
p = i;
continue;
}
}
fprintf(fp, "<a href='%s'>\n<img src='%s' alt='%s'></a><br>\n%s<br>\n", p->filename, p->filename, p->filename, GetDate(p->birthtime));
for (size_t i = 0; i < p->size; i++)
fprintf(fp, " • <a href='%s.html'>%s</a>", p->tags[i], p->tags[i]);
if (p->desc)
fprintf(fp, " %s", p->desc);
fputs("\n<br><br><br>\n", fp);
p = i;
images++;
if (images == POST_PER_PAGE)
break;
}
fputs("<div class='pager'><center>\n", fp);
for (size_t i = 0; i <= pages; i++) {
if (i == 0) {
if (i == page)
fprintf(fp, "<a id='curr' href='%s.html'>0</a> ", file);
else
fprintf(fp, "<a href='%s.html'>0</a> ", file);
}
else {
char *new_path = create_path("%s-%zu.html", file, i);
if (i == page)
fprintf(fp, "<a id='curr' href='%s'>%zu</a> ", new_path, i);
else
fprintf(fp, "<a href='%s'>%zu</a> ", new_path, i);
}
}
fprintf(fp, "\n</center></div>\n</body>\n</html>\n");
return p;
}
void build_html(const char *file, const struct UNIQ_TAGS ut, struct DB_STR *db) {
size_t page = 0;
size_t pages = 0;
/* Count pages */
size_t count = 0;
struct DB_STR *p = db;
while (p != NULL) {
struct DB_STR *i = p->next;
if (count > POST_PER_PAGE) {
count = 0;
pages++;
}
if (!strcmp(file, "index"))
count++;
else {
for (size_t i = 0; i < p->size; i++)
if (!strcmp(file, p->tags[i]))
count++;
}
p = i;
}
p = db;
while (1) {
char *new_path;
if (page == 0)
new_path = create_path("%s/%s.html", ROOT, file);
else
new_path = create_path("%s/%s-%zu.html", ROOT, file, page);
FILE *fp = file_open(new_path, "w");
if (fp == NULL)
return;
/* Builders */
p = build_html_page(fp, file, ut, p, page, pages);
if (p == NULL) {
fclose(fp);
break;
}
fclose(fp);
page++;
}
}
void build_xml(const char *file, struct DB_STR *db) {
char *new_path = create_path("%s/%s", ROOT, file);
FILE *fp = file_open(new_path, "w");
if (fp == NULL)
return;
/* Header */
fprintf(fp, "<?xml version='1.0' encoding='UTF-8' ?>\n<rss version='2.0' xmlns:atom='http://www.w3.org/2005/Atom'>\n<channel>\n\t<atom:link href='%s' rel='self' type='application/rss+xml' />\n\t<title>%s</title>\n\t<link>%s</link>\n\t<description>%s</description>\n\t<language>%s</language>\n", LINK, TITLE, LINK, DESC, LANGUAGE);
/* Content */
size_t count = 0;
struct DB_STR *p = db;
while (p) {
struct DB_STR *i = p->next;
/* Post */
fprintf(fp, "\n<item>\n\t<title>%s</title>\n\t<link>%s%s%s</link>\n\t<guid>%s%s%s</guid>\n\t<pubDate>\n\t\t%s\t</pubDate>\n\t<description><![CDATA[\n\t\t<p><img src='%s%s%s' alt='plup'></p>\n\t\t<p>",
p->filename,
LINK, CHECK_URL(LINK), p->filename,
LINK, CHECK_URL(LINK), p->filename,
GetDate(p->birthtime),
LINK, CHECK_URL(LINK), p->filename);
/* Tags */
for (size_t i = 0; i < p->size; i++)
fprintf(fp, "<a href='%s%s%s.html'> %s</a>&nbsp; ",
LINK, CHECK_URL(LINK), p->tags[i],
p->tags[i]);
fputs("</p>\n\t]]></description>\n</item>", fp);
if (count == MAX_POST_COUNT)
break;
p = i;
count++;
}
fputs("\n</channel>\n</rss>", fp);
fclose(fp);
}
char **uniq(struct DB_STR *db, size_t *uniq_size) {
char **uniq_tag = malloc(sizeof(char *));
if (uniq_tag == NULL)
return NULL;
struct DB_STR *p = db;
while (p != NULL) {
struct DB_STR *i = p->next;
for (size_t i = 0; i < p->size; i++) {
if (p->tags == NULL)
break;
char flag = 0;
for (size_t j = 0; j < *uniq_size; j++) {
if (!strcmp(p->tags[i], uniq_tag[j]))
flag = 1;
}
if (flag)
continue;
char **bckp = realloc(uniq_tag, sizeof(char *) * (*uniq_size + 1));
if (bckp == NULL) {
free(uniq_tag);
return NULL;
}
uniq_tag = bckp;
uniq_tag[*uniq_size] = p->tags[i];
(*uniq_size)++;
}
p = i;
}
return uniq_tag;
}
void rebuild(void) {
struct DB_STR *db = read_db();
if (db == NULL)
exit(1);
/* Uniq */
size_t uniq_size = 0;
char **uniq_tag = uniq(db, &uniq_size);
if (uniq_tag == NULL) {
fprintf(stderr, "8img: malloc: %s\n", strerror(errno));
free_db(db);
exit(1);
}
struct UNIQ_TAGS ut = {.tags = uniq_tag, .size = uniq_size};
for (size_t i = 0; i < uniq_size; i++)
build_html(uniq_tag[i], ut, db);
build_html("index", ut, db);
build_xml(XML_FILE, db);
/* Close */
free(uniq_tag);
free_db(db);
}
void help(int ret) {
puts("8img -[d:] [add/rebuild/version/del]:\n\tadd [img] [tag1] [tag2]...\n\trebuild\n\tversion\n\tdel [image id]\nOptions:\n\t-d STR set description");
exit(ret);
}
void del(const char *file) {
if (file == NULL)
help(1);
struct DB_STR *db = read_db();
if (db == NULL)
exit(1);
FILE *out = fopen(DB, "w");
if (out == NULL) {
free_db(db);
exit(1);
}
struct DB_STR *bckp = db;
while (bckp != NULL) {
struct DB_STR *ptr = bckp->next;
if (!strcmp(file, bckp->filename)) {
char *path = create_path("%s/%s", ROOT, bckp->filename);
remove(path);
bckp = ptr;
continue;
}
fprintf(out, "%s|", bckp->filename);
for (size_t i = 0; i < bckp->size; i++)
fprintf(out, "%s%c", bckp->tags[i], (i == bckp->size - 1) ? '|' : ',');
fprintf(out, "%s\n", bckp->desc);
bckp = ptr;
}
free_db(db);
fclose(out);
}
int main(int argc, char **argv) {
if (argc < 2)
help(0);
mkdir(ROOT, 0777);
srand(getpid());
char *desc = NULL;
int opt;
while ((opt = getopt(argc, argv, "d:t:")) != -1) {
switch (opt) {
case 'd':
desc = optarg;
break;
default:
help(1);
}
}
argv += optind;
argc -= optind;
/* Parse args */
if (!strcmp(argv[0], "add") && argc > 1)
add(argv + 1, argc - 1, desc);
else if (!strcmp(argv[0], "del"))
del(*(argv + 1));
else if (!strcmp(argv[0], "rebuild"))
rebuild();
else if (!strcmp(argv[0], "version"))
puts("8img version: 1.1\nWritten under WTFPL License.");
else
help(1);
return 0;
}

251
src/builder.c Normal file
View file

@ -0,0 +1,251 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include "funcs.h"
#include "config.h"
#include "db.h"
#define CHECK_URL(x) ( (sizeof(x) > 2 && x[sizeof(x) - 2] == '/') ? "" : "/" )
struct UNIQ_TAGS {
char **tags;
size_t size;
};
char *GetDate(const char *fmt, time_t time) {
struct tm *tm = localtime(&time);
static char buf[256];
if (strftime(buf, sizeof(buf) - 1, fmt, tm) > 0)
return buf;
return "Unknown";
}
struct DB_STR *build_html_page(const struct CONFIG cfg, FILE *fp, const char *file, const struct UNIQ_TAGS ut, struct DB_STR *db, const size_t page, const size_t pages) {
fprintf(fp, "<html>\n<head>\n<link rel='stylesheet' href='%s'>\n<title>%s</title>\n<meta charset='UTF-8'>\n</head>\n<body>\n<center><a href='index.html'><img id='logo' src='%s'></a>\n<h1 id='desc'>%s | <a href='%s'>RSS</a><br>%s/p%zu</h1></center>\n", CSS, TITLE, LOGO, DESC, XML_FILE, file, page);
/* Tags */
fputs("<center><div class='alltags'>\n<a href='index.html'>all</a>", fp);
for (size_t i = 0; i < ut.size; i++)
fprintf(fp, "<a href='%s.html'> %s</a>", ut.tags[i], ut.tags[i]);
fputs("\n</center></div><br><br>\n", fp);
/* Images */
size_t images = 0;
struct DB_STR *p = db;
while (p != NULL) {
struct DB_STR *i = p->next;
if (strcmp(file, "index")) {
char flag = 1;
for (size_t i = 0; i < p->size; i++)
if (!strcmp(p->tags[i], file))
flag = 0;
if (flag) {
p = i;
continue;
}
}
fprintf(fp, "<a href='%s'>\n<img src='%s' alt='%s'></a><br>\n%s<br>\n", p->filename, p->filename, p->filename, GetDate(cfg.date_fmt, p->birthtime));
for (size_t i = 0; i < p->size; i++)
fprintf(fp, " • <a href='%s.html'>%s</a>", p->tags[i], p->tags[i]);
if (p->desc)
fprintf(fp, " %s", p->desc);
fputs("\n<br><br><br>\n", fp);
p = i;
images++;
if (images == cfg.posts_at_page)
break;
}
fputs("<div class='pager'><center>\n", fp);
for (size_t i = 0; i <= pages; i++) {
if (i == 0) {
if (i == page)
fprintf(fp, "<a id='curr' href='%s.html'>0</a> ", file);
else
fprintf(fp, "<a href='%s.html'>0</a> ", file);
}
else {
char *new_path = create_path("%s-%zu.html", file, i);
if (i == page)
fprintf(fp, "<a id='curr' href='%s'>%zu</a> ", new_path, i);
else
fprintf(fp, "<a href='%s'>%zu</a> ", new_path, i);
}
}
fprintf(fp, "\n</center></div>\n</body>\n</html>\n");
return p;
}
void build_html(const struct CONFIG cfg, const char *file, const struct UNIQ_TAGS ut, struct DB_STR *db) {
size_t page = 0;
size_t pages = 0;
/* Count pages */
size_t count = 0;
struct DB_STR *p = db;
while (p != NULL) {
struct DB_STR *i = p->next;
if (count > cfg.posts_at_page) {
count = 0;
pages++;
}
if (!strcmp(file, "index"))
count++;
else {
for (size_t i = 0; i < p->size; i++)
if (!strcmp(file, p->tags[i]))
count++;
}
p = i;
}
p = db;
while (1) {
char *new_path;
if (page == 0)
new_path = create_path("%s.html", file);
else
new_path = create_path("%s-%zu.html", file, page);
FILE *fp = file_open(cfg.prog_name, new_path, "w");
if (fp == NULL)
return;
/* Builders */
p = build_html_page(cfg, fp, file, ut, p, page, pages);
if (p == NULL) {
fclose(fp);
break;
}
fclose(fp);
page++;
}
}
void build_xml(const struct CONFIG cfg, struct DB_STR *db) {
FILE *fp = file_open(cfg.prog_name, cfg.xml_file, "w");
if (fp == NULL)
return;
/* Header */
fprintf(fp, "<?xml version='1.0' encoding='UTF-8' ?>\n<rss version='2.0' xmlns:atom='http://www.w3.org/2005/Atom'>\n<channel>\n\t<atom:link href='%s' rel='self' type='application/rss+xml' />\n\t<title>%s</title>\n\t<link>%s</link>\n\t<description>%s</description>\n\t<language>%s</language>\n", LINK, TITLE, LINK, DESC, LANGUAGE);
/* Content */
size_t count = 0;
struct DB_STR *p = db;
while (p) {
struct DB_STR *i = p->next;
/* Post */
fprintf(fp, "\n<item>\n\t<title>%s</title>\n\t<link>%s%s%s</link>\n\t<guid>%s%s%s</guid>\n\t<pubDate>\n\t\t%s\t</pubDate>\n\t<description><![CDATA[\n\t\t<p><img src='%s%s%s' alt='plup'></p>\n\t\t<p>",
p->filename,
LINK, CHECK_URL(LINK), p->filename,
LINK, CHECK_URL(LINK), p->filename,
GetDate(cfg.date_fmt, p->birthtime),
LINK, CHECK_URL(LINK), p->filename);
/* Tags */
for (size_t i = 0; i < p->size; i++)
fprintf(fp, "<a href='%s%s%s.html'> %s</a>&nbsp; ",
LINK, CHECK_URL(LINK), p->tags[i],
p->tags[i]);
fputs("</p>\n\t]]></description>\n</item>", fp);
if (count == cfg.max_posts)
break;
p = i;
count++;
}
fputs("\n</channel>\n</rss>\n", fp);
fclose(fp);
}
char **uniq(struct DB_STR *db, size_t *uniq_size) {
char **uniq_tag = malloc(sizeof(char *));
if (uniq_tag == NULL)
return NULL;
struct DB_STR *p = db;
while (p != NULL) {
struct DB_STR *i = p->next;
for (size_t i = 0; i < p->size; i++) {
if (p->tags == NULL)
break;
char flag = 0;
for (size_t j = 0; j < *uniq_size; j++) {
if (!strcmp(p->tags[i], uniq_tag[j]))
flag = 1;
}
if (flag)
continue;
char **bckp = realloc(uniq_tag, sizeof(char *) * (*uniq_size + 1));
if (bckp == NULL) {
free(uniq_tag);
return NULL;
}
uniq_tag = bckp;
uniq_tag[*uniq_size] = p->tags[i];
(*uniq_size)++;
}
p = i;
}
return uniq_tag;
}
int rebuild(const struct CONFIG cfg) {
struct DB_STR *db = read_db(cfg);
if (db == NULL)
return 1;
/* Uniq */
size_t uniq_size = 0;
char **uniq_tag = uniq(db, &uniq_size);
if (uniq_tag == NULL) {
fprintf(stderr, "%s: malloc: %s\n", cfg.prog_name, strerror(errno));
free_db(db);
return 1;
}
struct UNIQ_TAGS ut = {.tags = uniq_tag, .size = uniq_size};
for (size_t i = 0; i < uniq_size; i++)
build_html(cfg, uniq_tag[i], ut, db);
build_html(cfg, "index", ut, db);
build_xml(cfg, db);
/* Close */
free(uniq_tag);
free_db(db);
return 0;
}

8
src/builder.h Normal file
View file

@ -0,0 +1,8 @@
#ifndef _BUILDER_H
#define _BUILDER_H
#include "config.h"
int rebuild(const struct CONFIG cfg);
#endif

7
src/config.c Normal file
View file

@ -0,0 +1,7 @@
#include "funcs.h"
#include "config.h"
int parse_config(struct CONFIG *cfg, const char *cfg_path) {
cfg->root = "www";
return 0;
}

80
src/config.h Normal file
View file

@ -0,0 +1,80 @@
#ifndef _CONFIG_H
#define _CONFIG_H
#include <string.h>
/* Default config */
#ifndef ROOT
#define ROOT "www"
#endif
/* HTML */
#ifndef CSS
#define CSS "style.css"
#endif
#ifndef LOGO
#define LOGO "logo.png"
#endif
#ifndef DESC
#define DESC "8img"
#endif
#ifndef TITLE
#define TITLE "/ img"
#endif
#ifndef POSTS_AT_PAGE
#define POST_AT_PAGE 5
#endif
/* RSS */
#ifndef LINK
#define LINK "https://nlight.tilde.team/img"
#endif
#ifndef XML_FILE
#define XML_FILE "index.xml"
#endif
#ifndef LANGUAGE
#define LANGUAGE "en-US"
#endif
#ifndef MAX_POSTS
#define MAX_POSTS 25
#endif
#ifndef DATE_FORMAT
#define DATE_FORMAT "%a, %d %h %Y %H:%M:%S %z"
#endif
struct CONFIG {
char allocated;
char *prog_name;
char *root;
/* HTML */
char *css;
char *logo;
char *desc;
char *title;
size_t posts_at_page;
/* RSS */
char *link;
char *xml_file;
char *language;
size_t max_posts;
/* Date format */
char *date_fmt;
};
int parse_config(struct CONFIG *cfg, const char *cfg_path);
#endif

158
src/db.c Normal file
View file

@ -0,0 +1,158 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <time.h>
#include "db.h"
#include "config.h"
#include "funcs.h"
void free_db(struct DB_STR *db) {
while (db != NULL) {
struct DB_STR *i = db->next;
if (db->filename != NULL)
free(db->filename);
if (db->desc != NULL)
free(db->desc);
if (db->tags != NULL) {
for (size_t i = 0; i < db->size; i++)
if (db->tags[i] != NULL)
free(db->tags[i]);
free(db->tags);
}
free(db);
db = i;
}
}
int append_tag(struct DB_STR *db, const char *tok, const size_t allocated) {
if (allocated == 0) {
db->tags = malloc(sizeof(char *) * (db->size + 1));
if (db->tags == NULL)
return 1;
db->tags[0] = strdup(tok);
if (db->tags[0] == NULL) {
free(db->tags);
return 1;
}
}
else {
db->tags[allocated] = strdup(tok);
if (db->tags[allocated] == NULL)
return 1;
}
return 0;
}
struct DB_STR *new_db_value(const struct CONFIG cfg, char *str) {
struct DB_STR *db = malloc(sizeof(struct DB_STR));
if (db == NULL)
return NULL;
/* Prepare string */
memset(db, 0, sizeof(struct DB_STR));
char *ptr = strrchr(str, '\n');
if (ptr != NULL)
*ptr = '\0';
/* Filename */
char *tok_rest = str;
char *tok = strtok_r(tok_rest, "|", &tok_rest);
if (tok != NULL) {
db->filename = strdup(tok);
if (db->filename == NULL)
goto NEW_DB_VALUE_CLOSE;
}
else
goto NEW_DB_VALUE_CLOSE;
/* Tags */
tok = strtok_r(tok_rest, "|", &tok_rest);
if (tok != NULL) {
db->size = 1;
for (size_t i = 0; i < strlen(tok); i++)
if (tok[i] == ',' && tok[i + 1])
db->size++;
size_t allocated = 0;
char *tok2_rest = tok;
char *tok2;
while ((tok2 = strtok_r(tok2_rest, ",", &tok2_rest)) != NULL) {
if (append_tag(db, tok2, allocated))
goto NEW_DB_VALUE_CLOSE;
allocated++;
}
}
/* Desc */
tok = strtok_r(tok_rest, "|", &tok_rest);
if (tok != NULL)
db->desc = strdup(tok);
/* Stats */
struct stat sb;
if (stat(db->filename, &sb) < 0)
fprintf(stderr, "%s: stat: %s: %s\n", cfg.prog_name, db->filename, strerror(errno));
db->birthtime = sb.st_mtime;
return db;
NEW_DB_VALUE_CLOSE:
free_db(db);
return NULL;
}
struct DB_STR *read_db(const struct CONFIG cfg) {
FILE *fp = file_open(cfg.prog_name, "db", "r");
if (fp == NULL)
return NULL;
int ret = 1;
char *line = NULL;
ssize_t bytes = 0;
size_t n = 0;
struct DB_STR *db = NULL;
struct DB_STR *i = NULL;
while ((bytes = getline(&line, &n, fp)) > 0) {
if (line[0] == '\0' || line[0] == '\n')
continue;
db = new_db_value(cfg, line);
if (db == NULL)
goto READ_DB_CLOSE;
db->next = i;
i = db;
}
ret = 0;
READ_DB_CLOSE:
if (ret) {
free_db(db);
fprintf(stderr, "%s: db: %s\n", cfg.prog_name, strerror(errno));
}
if (line)
free(line);
fclose(fp);
return db;
}

24
src/db.h Normal file
View file

@ -0,0 +1,24 @@
#ifndef _DB_H
#define _DB_H
#include <time.h>
#include "config.h"
struct DB_STR {
char *filename;
char **tags;
size_t size;
char *desc;
time_t birthtime;
struct DB_STR *next;
};
void free_db(struct DB_STR *db);
struct DB_STR *new_db_value(const struct CONFIG cfg, char *str);
struct DB_STR *read_db(const struct CONFIG cfg);
#endif

26
src/funcs.c Normal file
View file

@ -0,0 +1,26 @@
#include <stdio.h>
#include <limits.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
char *create_path(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
static char path[PATH_MAX + 1];
vsnprintf(path, sizeof(path) - 1, fmt, args);
va_end(args);
return path;
}
FILE *file_open(const char *prog_name, const char *file, const char *mode) {
FILE *fp = fopen(file, mode);
if (fp == NULL) {
fprintf(stderr, "%s: %s: %s\n", prog_name, file, strerror(errno));
return NULL;
}
return fp;
}

9
src/funcs.h Normal file
View file

@ -0,0 +1,9 @@
#ifndef _FUNCS_H
#define _FUNCS_H
#include <stdio.h>
char *create_path(const char *fmt, ...);
FILE *file_open(const char *prog_name, const char *file, const char *mode);
#endif

227
src/main.c Normal file
View file

@ -0,0 +1,227 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include "funcs.h"
#include "config.h"
#include "db.h"
#include "builder.h"
struct CONFIG cfg;
void help(void) {
////
printf("%s -[f:d:h] [add/rebuild/version/del]:\n\tadd [img] [tag1] [tag2]...\n\trebuild\n\tversion\n\tdel [image id]\n\nOptions:\n\t-d STR set description\n\t-f STR Path to config file\n\t-h This menu\n", cfg.prog_name);
exit(0);
}
char *gen_id(const char *filename) {
const char *alf = "qwertyuiopasdfghjklzxcvbnm1234567890QWERTYUIOPASDFGHJKLZXCVBNM";
char *ext = strrchr(filename, '.');
if (ext == NULL)
ext = "";
size_t size = 7 + strlen(ext);
char *id = malloc(size);
if (id == NULL) {
fprintf(stderr, "%s: malloc: %s\n", cfg.prog_name, strerror(errno));
return NULL;
}
for (size_t i = 0; i < size - strlen(ext); i++)
id[i] = alf[rand() % sizeof(alf)];
strcat(id, ext);
id[size] = '\0';
return id;
}
int copy(const char *src, const char *dst) {
FILE *ifp = file_open(cfg.prog_name, src, "r");
if (ifp == NULL)
return 1;
FILE *ofp = file_open(cfg.prog_name, dst, "w");
if (ofp == NULL) {
fclose(ifp);
return 1;
}
char buf[4096];
size_t ret = 0;
while ((ret = fread(buf, sizeof(char), sizeof(buf), ifp)))
fwrite(buf, sizeof(char), ret, ofp);
fclose(ifp);
fclose(ofp);
return 0;
}
int add(char **list, const int size, const char *desc) {
char *name = NULL;
/* Generate new filename (image id) */
while (1) {
name = gen_id(list[0]);
if (name == NULL)
return 1;
if (access(name, W_OK))
break;
free(name);
}
/* Copy file */
if (copy(list[0], name)) {
free(name);
return 1;
}
/* Update db */
FILE *fp = file_open(cfg.prog_name, "db", "a");
if (fp == NULL) {
free(name);
return 1;
}
/* Write "filename|tag1,tag2...|desc" to the db */
fprintf(fp, "%s|", name);
if (size > 1) {
for (int i = 1; i < size; i++) {
if (strchr(list[i], ',') == NULL || strchr(list[i], ' ') == NULL)
fprintf(fp, "%s%c", list[i], (i == size - 1) ? '|' : ',');
else
fprintf(stderr, "%s: tag '%s' contain ',', ' ' or '|'\n", cfg.prog_name, list[i]);
}
}
else
fputs("no_context|", fp);
if (desc)
fputs(desc, fp);
fputc('\n', fp);
free(name);
fclose(fp);
return 0;
}
int del(const char *str) {
struct DB_STR *db = read_db(cfg);
if (db == NULL)
return 1;
FILE *out = file_open(cfg.prog_name, "db", "w");
if (out == NULL) {
free_db(db);
return 1;
}
struct DB_STR *bckp = db;
while (bckp != NULL) {
struct DB_STR *ptr = bckp->next;
if (strstr(bckp->filename, str)) {
remove(bckp->filename);
bckp = ptr;
continue;
}
fprintf(out, "%s|", bckp->filename);
for (size_t i = 0; i < bckp->size; i++)
fprintf(out, "%s%c", bckp->tags[i], (i == bckp->size - 1) ? '|' : ',');
fprintf(out, "%s\n", bckp->desc);
bckp = ptr;
}
free_db(db);
fclose(out);
return 0;
}
int main(int argc, char **argv) {
srand(getpid());
char *desc = NULL;
cfg.prog_name = argv[0];
/* Default config */
cfg.root = ROOT;
cfg.css = CSS;
cfg.logo = LOGO;
cfg.desc = DESC;
cfg.title = TITLE;
cfg.posts_at_page = POST_AT_PAGE;
cfg.link = LINK;
cfg.xml_file = XML_FILE;
cfg.language = LANGUAGE;
cfg.max_posts = MAX_POSTS;
cfg.date_fmt = DATE_FORMAT;
/* Parse arguments */
int opt;
while ((opt = getopt(argc, argv, "f:d:h")) != -1) {
switch (opt) {
case 'd':
desc = optarg;
break;
case 'f':
if (parse_config(&cfg, optarg))
return 1;
break;
case 'h':
default:
help();
}
}
argv += optind;
argc -= optind;
if (chdir(cfg.root) < 0) {
fprintf(stderr, "%s: chdir: %s\n", cfg.prog_name, strerror(errno));
////
return 1;
}
int ret = 0;
/* Parse args */
if (!*argv)
help();
else if (argc > 1 && !strcmp(argv[0], "add"))
ret = add(argv + 1, argc - 1, desc);
else if (argc > 1 && !strcmp(argv[0], "del"))
ret = del(*(argv + 1));
else if (!strcmp(argv[0], "rebuild"))
ret = rebuild(cfg);
else if (!strcmp(argv[0], "version"))
printf("%s (8img) version: 2\nWritten under WTFPL License.\n", cfg.prog_name);
else
help();
////
return ret;
}

View file

Before

Width:  |  Height:  |  Size: 784 B

After

Width:  |  Height:  |  Size: 784 B

Before After
Before After