#ifndef H_FILTER #define H_FILTER #include #include #include #include #include #include #define MIN(a, b) (a > b? b : a) #define MAX(a, b) (a > b? a : b) struct filter { struct { char** ptr; size_t len; } tags; struct { struct { char** ptr; int* in; size_t len; } included; struct { char** ptr; size_t len; } excluded; } files; }; static inline int filter_include_count(struct filter* filter) { int count = 0; for (size_t i = 0; i < filter->tags.len; ++i) { if (filter->tags.ptr[i][0] != '@') count++; } return count; } static inline int filter_exclude_count(struct filter* filter) { int count = 0; for (size_t i = 0; i < filter->tags.len; ++i) { if (filter->tags.ptr[i][0] == '@') count++; } return count; } static inline void filter_free(struct filter* filter) { if (filter->tags.ptr) { for (size_t i = 0; i < filter->tags.len; ++i) { free(filter->tags.ptr[i]); } free(filter->tags.ptr); } if (filter->files.included.ptr) { for (size_t i = 0; i < filter->files.included.len; ++i) { free(filter->files.included.ptr[i]); } free(filter->files.included.ptr); free(filter->files.included.in); } if (filter->files.excluded.ptr) { for (size_t i = 0; i < filter->files.excluded.len; ++i) { free(filter->files.excluded.ptr[i]); } free(filter->files.excluded.ptr); } } static inline int load_filter_files(struct filter* filter, struct dirinfo* tagdir) { memset(&filter->files, 0, sizeof filter->files); for (size_t i = 0; i < filter->tags.len; ++i) { const char* tag = filter->tags.ptr[i]; int excluded = (*tag == '@'); int fd = openat(tagdir->fd, tag + (excluded? 1 : 0), O_DIRECTORY|O_RDONLY); if (fd == -1) return 0; DIR* dp = fdopendir(fd); struct dirent* ent; while ((ent = readdir(dp))) { if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) continue; char name[PATH_MAX]; if (readlinkat(fd, ent->d_name, name, sizeof name) == -1) { closedir(dp); close(fd); return 0; } const char* p = strrchr(name, '/') + 1; if (excluded) { size_t size = ++filter->files.excluded.len * sizeof *filter->files.excluded.ptr; void* ptr = realloc(filter->files.excluded.ptr, size); if (ptr == NULL) { closedir(dp); close(fd); return 0; } filter->files.excluded.ptr = ptr; char** str = &filter->files.excluded.ptr[filter->files.excluded.len - 1]; size = MIN(strlen(p), NAME_MAX); *str = malloc(size + 1); if (*str == NULL) { closedir(dp); close(fd); return 0; } strncpy(*str, p, size); (*str)[size] = '\0'; } else { int skip = 0; for (size_t j = 0; j < filter->files.included.len; ++j) { if (strcmp(filter->files.included.ptr[j], p) == 0) { skip = 1; filter->files.included.in[j] += 1; } } if (skip) continue; size_t size = ++filter->files.included.len * sizeof *filter->files.included.ptr; void* ptr = realloc(filter->files.included.ptr, size); if (ptr == NULL) { closedir(dp); close(fd); return 0; } filter->files.included.ptr = ptr; size = filter->files.included.len * sizeof *filter->files.included.in; ptr = realloc(filter->files.included.in, size); if (ptr == NULL) { closedir(dp); close(fd); return 0; } filter->files.included.in = ptr; filter->files.included.in[filter->files.included.len - 1] = 1; char** str = &filter->files.included.ptr[filter->files.included.len - 1]; size = MIN(strlen(p), NAME_MAX); *str = malloc(size + 1); if (*str == NULL) { closedir(dp); close(fd); return 0; } strncpy(*str, p, size); (*str)[size] = '\0'; } } closedir(dp); close(fd); } return 1; } static inline int load_filter_tags(int fd, struct filter* filter) { FILE* fp = fdopen(fd, "r"); char tag[TAG_MAX + 1]; memset(&filter->tags, 0, sizeof filter->tags); while (fgets(tag, sizeof tag, fp)) { size_t size = ++filter->tags.len * sizeof *filter->tags.ptr; void* ptr = realloc(filter->tags.ptr, size); if (ptr == NULL) { fclose(fp); return 0; } filter->tags.ptr = ptr; char** str = &filter->tags.ptr[filter->tags.len - 1]; size = MIN(strlen(tag), TAG_MAX); *str = malloc(size + 1); if (*str == NULL) { fclose(fp); return 0; } if (tag[size - 1] == '\n') tag[size - 1] = '\0'; strncpy(*str, tag, size); (*str)[size] = '\0'; } fclose(fp); return 1; } #endif