#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(void) { char *alf = "qwertyUIOPasdfghjklQWERTASDFGHJKLZXCVBNMzxcvbnm1234567890"; static char id[6]; for (size_t i = 0; i < sizeof(id); i++) id[i] = alf[rand() % sizeof(alf)]; id[sizeof(id) - 1] = '\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(); 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) 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); 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", CSS, TITLE); /* Tags */ fputs("\n

\n [all] ", 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


\n", 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; } 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"); 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"); 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 help(); }