From 77ade504f673c5106d25069fd44054b37484d7fb Mon Sep 17 00:00:00 2001
From: Your Name
Date: Sun, 8 Jun 2025 20:52:43 +0300
Subject: [PATCH] config parser (WIP)
---
config | 20 ++
config.h | 23 --
img.c | 609 -------------------------------------
src/builder.c | 251 +++++++++++++++
src/builder.h | 8 +
src/config.c | 7 +
src/config.h | 80 +++++
src/db.c | 158 ++++++++++
src/db.h | 24 ++
src/funcs.c | 26 ++
src/funcs.h | 9 +
src/main.c | 227 ++++++++++++++
logo.png => www/logo.png | Bin
style.css => www/style.css | 0
14 files changed, 810 insertions(+), 632 deletions(-)
create mode 100644 config
delete mode 100644 config.h
delete mode 100644 img.c
create mode 100644 src/builder.c
create mode 100644 src/builder.h
create mode 100644 src/config.c
create mode 100644 src/config.h
create mode 100644 src/db.c
create mode 100644 src/db.h
create mode 100644 src/funcs.c
create mode 100644 src/funcs.h
create mode 100644 src/main.c
rename logo.png => www/logo.png (100%)
rename style.css => www/style.css (100%)
diff --git a/config b/config
new file mode 100644
index 0000000..037e767
--- /dev/null
+++ b/config
@@ -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
diff --git a/config.h b/config.h
deleted file mode 100644
index ace24d6..0000000
--- a/config.h
+++ /dev/null
@@ -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"
diff --git a/img.c b/img.c
deleted file mode 100644
index 3bb134d..0000000
--- a/img.c
+++ /dev/null
@@ -1,609 +0,0 @@
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#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, "\n\n\n%s\n\n\n\n
\n%s | RSS
%s/p%zu
\n", CSS, TITLE, LOGO, DESC, XML_FILE, file, page);
-
- /* Tags */
- fputs("
\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, "\n
\n%s
\n", p->filename, p->filename, p->filename, GetDate(p->birthtime));
- for (size_t i = 0; i < p->size; i++)
- fprintf(fp, " • %s", p->tags[i], p->tags[i]);
-
- if (p->desc)
- fprintf(fp, " %s", p->desc);
-
- fputs("\n
\n", fp);
-
- p = i;
- images++;
- if (images == POST_PER_PAGE)
- break;
- }
-
- fputs("\n\n\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, "\n\n\n\t\n\t%s\n\t%s\n\t%s\n\t%s\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- \n\t%s\n\t%s%s%s\n\t%s%s%s\n\t\n\t\t%s\t\n\t

\n\t\t",
- 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, " %s ",
- LINK, CHECK_URL(LINK), p->tags[i],
- p->tags[i]);
-
- fputs("
\n\t]]>\n", fp);
- if (count == MAX_POST_COUNT)
- break;
-
- p = i;
- count++;
- }
-
- fputs("\n\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;
-}
-
-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;
-}
diff --git a/src/builder.c b/src/builder.c
new file mode 100644
index 0000000..537e624
--- /dev/null
+++ b/src/builder.c
@@ -0,0 +1,251 @@
+#include
+#include
+#include
+#include
+#include
+#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, "\n\n\n%s\n\n\n\n
\n%s | RSS
%s/p%zu
\n", CSS, TITLE, LOGO, DESC, XML_FILE, file, page);
+
+ /* Tags */
+ fputs("
\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, "\n
\n%s
\n", p->filename, p->filename, p->filename, GetDate(cfg.date_fmt, p->birthtime));
+ for (size_t i = 0; i < p->size; i++)
+ fprintf(fp, " • %s", p->tags[i], p->tags[i]);
+
+ if (p->desc)
+ fprintf(fp, " %s", p->desc);
+
+ fputs("\n
\n", fp);
+
+ p = i;
+ images++;
+ if (images == cfg.posts_at_page)
+ break;
+ }
+
+ fputs("\n\n\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, "\n\n\n\t\n\t%s\n\t%s\n\t%s\n\t%s\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- \n\t%s\n\t%s%s%s\n\t%s%s%s\n\t\n\t\t%s\t\n\t
\n\t\t",
+ 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, " %s ",
+ LINK, CHECK_URL(LINK), p->tags[i],
+ p->tags[i]);
+
+ fputs("
\n\t]]>\n ", fp);
+ if (count == cfg.max_posts)
+ break;
+
+ p = i;
+ count++;
+ }
+
+ fputs("\n\n\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;
+}
diff --git a/src/builder.h b/src/builder.h
new file mode 100644
index 0000000..8f82ba5
--- /dev/null
+++ b/src/builder.h
@@ -0,0 +1,8 @@
+#ifndef _BUILDER_H
+#define _BUILDER_H
+
+#include "config.h"
+
+int rebuild(const struct CONFIG cfg);
+
+#endif
diff --git a/src/config.c b/src/config.c
new file mode 100644
index 0000000..062b5d4
--- /dev/null
+++ b/src/config.c
@@ -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;
+}
diff --git a/src/config.h b/src/config.h
new file mode 100644
index 0000000..329cc6a
--- /dev/null
+++ b/src/config.h
@@ -0,0 +1,80 @@
+#ifndef _CONFIG_H
+#define _CONFIG_H
+
+#include
+
+/* 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
diff --git a/src/db.c b/src/db.c
new file mode 100644
index 0000000..6083980
--- /dev/null
+++ b/src/db.c
@@ -0,0 +1,158 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#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;
+}
diff --git a/src/db.h b/src/db.h
new file mode 100644
index 0000000..4f4c1b1
--- /dev/null
+++ b/src/db.h
@@ -0,0 +1,24 @@
+#ifndef _DB_H
+#define _DB_H
+
+#include
+#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
diff --git a/src/funcs.c b/src/funcs.c
new file mode 100644
index 0000000..254c333
--- /dev/null
+++ b/src/funcs.c
@@ -0,0 +1,26 @@
+#include
+#include
+#include
+#include
+#include
+
+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;
+}
diff --git a/src/funcs.h b/src/funcs.h
new file mode 100644
index 0000000..a80661c
--- /dev/null
+++ b/src/funcs.h
@@ -0,0 +1,9 @@
+#ifndef _FUNCS_H
+#define _FUNCS_H
+
+#include
+
+char *create_path(const char *fmt, ...);
+FILE *file_open(const char *prog_name, const char *file, const char *mode);
+
+#endif
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..778b30f
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,227 @@
+#include
+#include
+#include
+#include
+#include
+#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;
+}
+
diff --git a/logo.png b/www/logo.png
similarity index 100%
rename from logo.png
rename to www/logo.png
diff --git a/style.css b/www/style.css
similarity index 100%
rename from style.css
rename to www/style.css