From 3dcd9520d5060b8b0094bd471706cd98a4deefb3 Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 24 Jan 2025 20:30:34 +0300 Subject: [PATCH] first commit --- LICENSE | 11 ++ Makefile | 8 + README.md | 2 + config.h | 12 ++ img.c | 474 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ logo.png | Bin 0 -> 4283 bytes style.css | 36 +++++ 7 files changed, 543 insertions(+) create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 config.h create mode 100644 img.c create mode 100644 logo.png create mode 100644 style.css diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7a3094a --- /dev/null +++ b/LICENSE @@ -0,0 +1,11 @@ +DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +Version 2, December 2004 + +Copyright (C) 2004 Sam Hocevar + +Everyone is permitted to copy and distribute verbatim or modified copies of this license document, and changing it is allowed as long as the name is changed. + +DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHAT THE FUCK YOU WANT TO. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0e29fa3 --- /dev/null +++ b/Makefile @@ -0,0 +1,8 @@ +CC?=cc +CFLAGS?=-pedantic -Wall -Wextra -s -Os + +all: + $(CC) $(CFLAGS) img.c -o 8img + +clean: + rm 8img diff --git a/README.md b/README.md new file mode 100644 index 0000000..67d31e8 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# 8img + diff --git a/config.h b/config.h new file mode 100644 index 0000000..867865e --- /dev/null +++ b/config.h @@ -0,0 +1,12 @@ +/* Absolute path to */ +#define ROOT "." +#define DB ROOT"/db" + +/* Html generator */ +#define CSS "style.css" +#define LOGO "logo.jpg" + +#define DESC "from outer space" +#define TITLE "8img gallery" +#define POST_PER_PAGE 5 + diff --git a/img.c b/img.c new file mode 100644 index 0000000..5398da9 --- /dev/null +++ b/img.c @@ -0,0 +1,474 @@ +#include +#include +#include +#include +#include +#include +#include +#include "config.h" + +struct DB_STR { + char *filename; + + char **tags; + size_t size; + + struct DB_STR *next; +}; + +struct UNIQ_TAGS { + char **tags; + size_t size; +}; + +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, "a"); + 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) { + char *name = NULL; + char new_path[PATH_MAX + 1]; + + while (1) { + name = gen_id(list[0]); + if (name == NULL) { + fprintf(stderr, "8img: malloc: %s\n", strerror(errno)); + exit(1); + } + + snprintf(new_path, sizeof(new_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..." to the db */ + fprintf(fp, "%s ", name); + for (int i = 1; i < size; i++) { + if (strchr(list[i], ',') == NULL || strchr(list[i], ' ') == NULL) + fprintf(fp, ",%s", list[i]); + + else + fprintf(stderr, "8img: tag '%s' contain ',' or ' '\n", list[i]); + } + + fputc('\n', fp); + free(name); + fclose(fp); +} + +void free_db(struct DB_STR *db) { + if (db == NULL) + return; + + while (db != NULL) { + struct DB_STR *i = db->next; + if (i == NULL) + return; + + if (db->filename != NULL) + free(db->filename); + + 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; + + + /* Filename */ + char *tok = strtok(str, " "); + char *ptr = strrchr(tok, '\n'); + if (ptr != NULL) + *ptr = '\0'; + + db->filename = strdup(tok); + if (db->filename == NULL) + goto NDV_CLOSE; + + + /* Tags */ + tok = strtok(NULL, " "); + if (tok == NULL) + db->tags = NULL; + + else { + ptr = strrchr(tok, '\n'); + if (ptr != NULL) + *ptr = '\0'; + + for (size_t i = 0; i < strlen(tok); i++) + if (tok[i] == ',') + db->size++; + + size_t allocated = 0; + char *tok2 = strtok(tok, ","); + while (tok2 != NULL) { + if (append_tag(db, tok2, allocated)) + goto NDV_CLOSE; + + allocated++; + tok2 = strtok(NULL, ","); + } + } + + + return db; + +NDV_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))) { + if (bytes == -1) + break; + + else if (bytes <= 1) + continue; + + db = new_db_value(line); + if (db == NULL) + goto RDB_CLOSE; + + db->next = i; + i = db; + } + + ret = 0; + +RDB_CLOSE: + fclose(fp); + if (line) + free(line); + + if (ret) { + free_db(db); + fprintf(stderr, "8img: db: %s\n", strerror(errno)); + } + + return db; +} + +struct DB_STR *build_html(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

%s
%s/p%zu

\n
", CSS, TITLE, LOGO, DESC, 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, "%s
", p->filename, p->filename, p->filename); + for (size_t i = 0; i < p->size; i++) + fprintf(fp, " %s", p->tags[i], p->tags[i]); + + fputs("\n


\n", fp); + + p = i; + images++; + if (images >= POST_PER_PAGE) + break; + } + + fputs("\n
\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[PATH_MAX + 1]; + snprintf(new_path, sizeof(new_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\n"); + return p; +} + +void build(const char *file, const struct UNIQ_TAGS ut, struct DB_STR *db) { + FILE *fp = NULL; + + 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) { + if (fp != NULL) + fclose(fp); + + char new_path[PATH_MAX + 1]; + if (page == 0) + snprintf(new_path, sizeof(new_path), "%s/%s.html", ROOT, file); + + else + snprintf(new_path, sizeof(new_path), "%s/%s-%zu.html", ROOT, file, page); + + FILE *fp = file_open(new_path, "w"); + if (fp == NULL) + return; + + p = build_html(fp, file, ut, p, page, pages); + if (p == NULL) { + fclose(fp); + break; + } + + page++; + } +} + +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) + return; + + /* Uniq */ + size_t uniq_size = 0; + char **uniq_tag = uniq(db, &uniq_size); + if (uniq_tag != NULL) { + struct UNIQ_TAGS ut = {.tags = uniq_tag, .size = uniq_size}; + + for (size_t i = 0; i < uniq_size; i++) + build(uniq_tag[i], ut, db); + + build("index", ut, db); + } + + else + fprintf(stderr, "8img: malloc: %s\n", strerror(errno)); + + /* Close */ + if (uniq_tag != NULL) + free(uniq_tag); + + free_db(db); +} + +void help(void) { + puts("8img [add/rebuild]:\n\tadd [img] [tag1] [tag2] [...]\n\trebuild\n\tversion"); + exit(0); +} + +int main(int argc, char **argv) { + if (argc < 2) + help(); + + mkdir(ROOT, 0777); + srand(getpid()); + + argv++; + argc--; + + /* Parse args */ + if (!strcmp(argv[0], "add") && argc > 1) + add(argv + 1, argc - 1); + + else if (!strcmp(argv[0], "rebuild")) + rebuild(); + + else if (!strcmp(argv[0], "version")) + puts("8img version: 1.0.1\nWritten under WTFPL License."); + + else + help(); + + return 0; +} diff --git a/logo.png b/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..93eecb4be7f4ecbaf97241adbbaee35343638a0f GIT binary patch literal 4283 zcmeHKeQ*lr>AxtAE(uyKA>o z#8apBWbdk_?(3acmOdd?@Y-qJoTjFgEwAspMz=g`ISUi}67H-hUUcR0y(X0^;Q?r~ zxg9p!U_vO3hCSO`9zO1#v8nysM_d2-qG^lmSD(5(sfmX&?@d2Z)EV26nl`iU{@NWq zbN`)smz;l3XJN1~c)R_>&d&5FOnnPBhx>oLoPs};^l|Gt6)aFUUs+J{T=TVW=a#S? z+HbJ*TgR^M`6#+{O-s$O?`jHfbw7V#`bn_3W~aU8qZ0Z#nBP_~Kem_jju<9c~~yj(fXm(vs9x_?P#nsZZza-0;lP z4`;&6mo7g}tBw2fT-we1_vO9;-??<~mZh=gE2@5{ExSv6&n6l4h^5{YOdv{nrH2+C|W6QqvN>2QR=!&L!^i{gRs zLYX4YVFzJ81cMS31DMRodBh6Is?nfvY%o55(CHka4}=F)Kza~SE=W*XlJNV9ku$P-}_H|xm}bOj(3M*SvJQlyqI$;0knAxbnBoU{zNC}c>~FKjWkYrjRsUcFTg#3r*PT;1U*ga3|>!M)gkt<=#?T| z2xRz>jz}xior+f2V!426qeF?50l7LzFPt>u!+KEzdJzNp67r5Qn6QQTA4x0q0vrF|-v9NQT1wC4U2@_$6In*tnB1={qjhxLjjWU`*ig?i!bCObUz%Jl1>S>KD-n#O{iF03X zTz>xgk(&*l|GlsNRL<@n+-u%%rs!uYFETd~iSJh|J zH>Pdq?47Be55C{gUD9yLWnQuK$&U}Hk33MjEw5zFmd$5Bm@MR+`E}1Xv6|a?ceHJm y@27ow;B)n>!9Ul2SrO@fz3%FZo93nVzucd21G-_PY2qU&OO+$bWpB