2024-11-10 09:38:38 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#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;
|
|
|
|
};
|
|
|
|
|
2024-11-10 17:22:33 +00:00
|
|
|
char *gen_id(const char *filename) {
|
2024-11-11 09:53:11 +00:00
|
|
|
const char *alf = "qwertyuiopasdfghjklzxcvbnmZXCVBNMASDFGHJKLQWERTYUIOP1234567890";
|
2024-11-10 09:38:38 +00:00
|
|
|
|
2024-11-10 17:22:33 +00:00
|
|
|
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++)
|
2024-11-10 09:38:38 +00:00
|
|
|
id[i] = alf[rand() % sizeof(alf)];
|
|
|
|
|
2024-11-10 17:22:33 +00:00
|
|
|
strcat(id, ext);
|
|
|
|
id[size] = '\0';
|
2024-11-10 09:38:38 +00:00
|
|
|
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) {
|
2024-11-10 17:22:33 +00:00
|
|
|
name = gen_id(list[0]);
|
|
|
|
if (name == NULL) {
|
|
|
|
fprintf(stderr, "8img: malloc: %s\n", strerror(errno));
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2024-11-10 09:38:38 +00:00
|
|
|
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");
|
2024-11-10 18:14:30 +00:00
|
|
|
if (fp == NULL) {
|
|
|
|
free(name);
|
2024-11-10 09:38:38 +00:00
|
|
|
exit(1);
|
2024-11-10 18:14:30 +00:00
|
|
|
}
|
2024-11-10 09:38:38 +00:00
|
|
|
|
|
|
|
/* 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);
|
2024-11-10 18:14:30 +00:00
|
|
|
free(name);
|
2024-11-10 09:38:38 +00:00
|
|
|
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) {
|
2024-11-11 09:53:11 +00:00
|
|
|
fprintf(fp, "<html>\n<head>\n<link rel='stylesheet' href='%s'>\n<title>%s</title>\n</head>\n<body>\n<a href='index.html'><img id='logo' src='%s'></a><h1 id='desc'>%s<br>%s/p%zu</h1><center>", CSS, TITLE, LOGO, DESC, file, page);
|
2024-11-10 09:38:38 +00:00
|
|
|
|
|
|
|
/* Tags */
|
2024-11-11 09:53:11 +00:00
|
|
|
fputs("\n<a id='alltags' href='index.html'>all</a>", fp);
|
2024-11-10 09:38:38 +00:00
|
|
|
for (size_t i = 0; i < ut.size; i++)
|
2024-11-11 09:53:11 +00:00
|
|
|
fprintf(fp, "<a id='alltags' href='%s.html'> %s</a>", ut.tags[i], ut.tags[i]);
|
|
|
|
fputs("\n<br><br><div class='bar'></div><br>\n", fp);
|
2024-11-10 09:38:38 +00:00
|
|
|
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-10 14:26:37 +00:00
|
|
|
fprintf(fp, "<a href='%s'><img src='%s' alt='%s'></a><br>", p->filename, p->filename, p->filename);
|
2024-11-10 09:38:38 +00:00
|
|
|
for (size_t i = 0; i < p->size; i++)
|
2024-11-11 09:53:11 +00:00
|
|
|
fprintf(fp, "<a class='tags' href='%s.html'> %s</a>", p->tags[i], p->tags[i]);
|
2024-11-10 09:38:38 +00:00
|
|
|
|
2024-11-11 09:53:11 +00:00
|
|
|
fputs("\n<br><br><div class='bar'></div><br>\n", fp);
|
2024-11-10 09:38:38 +00:00
|
|
|
|
|
|
|
p = i;
|
|
|
|
images++;
|
|
|
|
if (images >= POST_PER_PAGE)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2024-11-11 09:53:11 +00:00
|
|
|
fputs("<div class='pager'>\n", fp);
|
2024-11-10 09:57:10 +00:00
|
|
|
for (size_t i = 0; i <= pages; i++) {
|
2024-11-10 09:38:38 +00:00
|
|
|
if (i == 0) {
|
|
|
|
if (i == page)
|
2024-11-11 09:53:11 +00:00
|
|
|
fprintf(fp, "<a id='curr' href='%s.html'>0</a> ", file);
|
2024-11-10 09:38:38 +00:00
|
|
|
|
|
|
|
else
|
2024-11-11 09:53:11 +00:00
|
|
|
fprintf(fp, "<a href='%s.html'>0</a> ", file);
|
2024-11-10 09:38:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
else {
|
|
|
|
char new_path[PATH_MAX + 1];
|
|
|
|
snprintf(new_path, sizeof(new_path), "%s-%zu.html", file, i);
|
|
|
|
|
|
|
|
if (i == page)
|
2024-11-11 09:53:11 +00:00
|
|
|
fprintf(fp, "<a id='curr' href='%s'>%zu</a>", new_path, i);
|
2024-11-10 09:38:38 +00:00
|
|
|
|
|
|
|
else
|
2024-11-11 09:53:11 +00:00
|
|
|
fprintf(fp, "<a href='%s'> %zu </a>", new_path, i);
|
2024-11-10 09:38:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2024-11-11 09:53:11 +00:00
|
|
|
fprintf(fp, "\n</div>\n</center>\n</body>\n</html>\n");
|
2024-11-10 09:38:38 +00:00
|
|
|
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;
|
|
|
|
|
2024-11-10 09:57:10 +00:00
|
|
|
if (count == POST_PER_PAGE) {
|
|
|
|
count = 0;
|
|
|
|
pages++;
|
|
|
|
}
|
|
|
|
|
2024-11-10 09:38:38 +00:00
|
|
|
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) {
|
2024-11-10 18:14:30 +00:00
|
|
|
puts("8img [add/rebuild]:\n\tadd [img] [tag1] [tag2] [...]\n\trebuild\n\tversion");
|
2024-11-10 09:38:38 +00:00
|
|
|
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();
|
|
|
|
|
2024-11-11 09:53:11 +00:00
|
|
|
else if (!strcmp(argv[0], "version"))
|
2024-11-10 18:14:30 +00:00
|
|
|
puts("8img version: 1.0\nWritten under WTFPL License.");
|
|
|
|
|
2024-11-10 09:38:38 +00:00
|
|
|
else
|
|
|
|
help();
|
2024-11-10 18:14:30 +00:00
|
|
|
|
|
|
|
return 0;
|
2024-11-10 09:38:38 +00:00
|
|
|
}
|