// vim: set ts=8 sw=8 noet tw=80: /** * gallery.c - Claudio Maggioni * * External sources of information used: * - man section 3 for the several library functions; * - libpng.org documentation of PNG header an IHDR; * - https://codereview.stackexchange.com/q/149751 for inspiration of ntohl * function implementation. */ #include "gallery.h" #include #include #include #include struct image { uint32_t width; uint32_t height; char * name; }; struct image_ll { struct image_ll* prev; struct image first; struct image_ll* next; }; struct gallery { size_t size; struct image_ll* images; }; static struct image_ll* find_by_name(struct gallery*, const char*); static uint32_t my_ntohl (uint32_t); static struct image_ll* ll_remove(struct gallery*, struct image_ll*); const size_t INIT_CAP = 128; const uint8_t HEADER[8] = { '\211', 'P', 'N', 'G', '\r', '\n', '\032', '\n' }; struct image_ll* find_by_name(struct gallery* g, const char* name) { for (struct image_ll* i = g->images; i; i = i->next) { if (i->first.name == name || !strcmp(i->first.name, name)) { return i; } } return NULL; } struct image_ll* ll_remove(struct gallery* g, struct image_ll* i) { if (i->next) { i->next->prev = i->prev; } if (i->prev) { i->prev->next = i->next; } else { g->images = i->next; } struct image_ll* n = i->next; free(i); g->size--; return n; } // inspired by https://codereview.stackexchange.com/q/149751 uint32_t my_ntohl (const uint32_t nlong) { uint8_t* data = (uint8_t*) &nlong; return ((uint32_t) data[3]) | ((uint32_t) data[2] << 8) | ((uint32_t) data[1] << 16) | ((uint32_t) data[0] << 24); } struct gallery *gallery_new() { struct gallery* g = malloc(sizeof(struct gallery)); if (!g) { return NULL; } g->images = NULL; g->size = 0; return g; } void gallery_destroy(struct gallery *g) { while (g->images) { struct image_ll* i = g->images; g->images = g->images->next; free(i); } free(g); } /* * remark: gallery_add(...) returns undocumented error code -3 when the malloc * for the new image data struct fails */ int gallery_add(struct gallery *g, char* filename) { if (find_by_name(g, filename)) { return -1; } FILE* f = fopen(filename, "r"); if (!f) { return -2; } uint8_t file_head[8]; if (fread(&file_head, 1, 8, f) != 8) { fclose(f); return -2; } if (memcmp(file_head, HEADER, sizeof(HEADER))) { return -2; } if (fseek(f, 8, SEEK_CUR) == -1) { perror("fseek failed"); fclose(f); return -2; } struct image_ll* new = malloc(sizeof(struct image_ll)); if (!new) { fclose(f); return -3; } if (fread(&new->first, 8, 1, f) != 1) { free(new); fclose(f); return -2; } new->first.width = my_ntohl(new->first.width); new->first.height = my_ntohl(new->first.height); new->first.name = filename; new->prev = NULL; new->next = g->images; if (g->images) { g->images->prev = new; } g->images = new; g->size++; return 1; } int gallery_rm(struct gallery* g, char* filename) { struct image_ll* i = find_by_name(g, filename); if (!i) { return 0; } ll_remove(g, i); return 1; } int gallery_count(struct gallery* g) { return g->size; } int gallery_filter(struct gallery *g, int (*f)(char *name, int w, int h)) { for (struct image_ll* i = g->images; i;) { if (!f(i->first.name, i->first.width, i->first.height)) { i = ll_remove(g, i); } else { i = i->next; } } return g->size; } char* gallery_bestfit(struct gallery* g, int max_width, int max_height) { struct image* best = NULL; for (struct image_ll* i = g->images; i; i = i->next) { uint32_t w = i->first.width; uint32_t h = i->first.height; if (w > max_width || h > max_height) { continue; } if (!best || w * h > best->width * best->height) { best = &i->first; } } return best ? best->name : NULL; }