tagfs/src/filter.h

239 lines
4.5 KiB
C

#ifndef H_FILTER
#define H_FILTER
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>
#include <limits.h>
#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