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("
\nall", fp); - for (size_t i = 0; i < ut.size; i++) - fprintf(fp, " %s", ut.tags[i], ut.tags[i]); - - fputs("\n


\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%s
\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", fp); - for (size_t i = 0; i <= pages; i++) { - if (i == 0) { - if (i == page) - fprintf(fp, "0 ", file); - - else - fprintf(fp, "0 ", file); - } - - else { - char *new_path = create_path("%s-%zu.html", file, i); - - if (i == page) - fprintf(fp, "%zu ", new_path, i); - - else - fprintf(fp, "%zu ", new_path, i); - } - - } - - fprintf(fp, "\n
\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\tplup

\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("
\nall", fp); + for (size_t i = 0; i < ut.size; i++) + fprintf(fp, " %s", ut.tags[i], ut.tags[i]); + + fputs("\n


\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%s
\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", fp); + for (size_t i = 0; i <= pages; i++) { + if (i == 0) { + if (i == page) + fprintf(fp, "0 ", file); + + else + fprintf(fp, "0 ", file); + } + + else { + char *new_path = create_path("%s-%zu.html", file, i); + + if (i == page) + fprintf(fp, "%zu ", new_path, i); + + else + fprintf(fp, "%zu ", new_path, i); + } + } + + fprintf(fp, "\n
\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\tplup

\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