/* * Copyright (C) 2008-2017 Tobias Brunner * Copyright (C) 2007 Martin Willi * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See . * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ #include "enumerator.h" #include #include #include #include #include #include #include #include #ifdef HAVE_GLOB_H #include #endif /* HAVE_GLOB_H */ #include /* * Described in header. */ bool enumerator_enumerate_default(enumerator_t *enumerator, ...) { va_list args; bool result; if (!enumerator->venumerate) { DBG1(DBG_LIB, "!!! ENUMERATE DEFAULT: venumerate() missing !!!"); return FALSE; } va_start(args, enumerator); result = enumerator->venumerate(enumerator, args); va_end(args); return result; } METHOD(enumerator_t, enumerate_empty, bool, enumerator_t *enumerator, va_list args) { return FALSE; } /* * Described in header */ enumerator_t* enumerator_create_empty() { enumerator_t *this; INIT(this, .enumerate = enumerator_enumerate_default, .venumerate = _enumerate_empty, .destroy = (void*)free, ); return this; } /** * Enumerator implementation for directory enumerator */ typedef struct { /** implements enumerator_t */ enumerator_t public; /** directory handle */ DIR *dir; /** absolute path of current file */ char full[PATH_MAX]; /** where directory part of full ends and relative file gets written */ char *full_end; } dir_enum_t; METHOD(enumerator_t, destroy_dir_enum, void, dir_enum_t *this) { closedir(this->dir); free(this); } METHOD(enumerator_t, enumerate_dir_enum, bool, dir_enum_t *this, va_list args) { struct dirent *entry = readdir(this->dir); struct stat *st; size_t remaining; char **relative, **absolute; int len; VA_ARGS_VGET(args, relative, absolute, st); if (!entry) { return FALSE; } if (streq(entry->d_name, ".") || streq(entry->d_name, "..")) { return this->public.enumerate(&this->public, relative, absolute, st); } if (relative) { *relative = entry->d_name; } if (absolute || st) { remaining = sizeof(this->full) - (this->full_end - this->full); len = snprintf(this->full_end, remaining, "%s", entry->d_name); if (len < 0 || len >= remaining) { DBG1(DBG_LIB, "buffer too small to enumerate file '%s'", entry->d_name); return FALSE; } if (absolute) { *absolute = this->full; } if (st) { if (stat(this->full, st)) { DBG1(DBG_LIB, "stat() on '%s' failed: %s", this->full, strerror(errno)); return FALSE; } } } return TRUE; } /* * Described in header */ enumerator_t* enumerator_create_directory(const char *path) { dir_enum_t *this; int len; INIT(this, .public = { .enumerate = enumerator_enumerate_default, .venumerate = _enumerate_dir_enum, .destroy = _destroy_dir_enum, }, ); if (*path == '\0') { path = "./"; } len = snprintf(this->full, sizeof(this->full)-1, "%s", path); if (len < 0 || len >= sizeof(this->full)-1) { DBG1(DBG_LIB, "path string '%s' too long", path); free(this); return NULL; } /* append a '/' if not already done */ if (this->full[len-1] != '/') { this->full[len++] = '/'; this->full[len] = '\0'; } this->full_end = &this->full[len]; this->dir = opendir(path); if (!this->dir) { DBG1(DBG_LIB, "opening directory '%s' failed: %s", path, strerror(errno)); free(this); return NULL; } return &this->public; } #ifdef HAVE_GLOB_H /** * Enumerator implementation for glob enumerator */ typedef struct { /** implements enumerator_t */ enumerator_t public; /** glob data */ glob_t glob; /** iteration count */ u_int pos; /** absolute path of current file */ char full[PATH_MAX]; } glob_enum_t; METHOD(enumerator_t, destroy_glob_enum, void, glob_enum_t *this) { globfree(&this->glob); free(this); } METHOD(enumerator_t, enumerate_glob_enum, bool, glob_enum_t *this, va_list args) { struct stat *st; char *match; char **file; VA_ARGS_VGET(args, file, st); if (this->pos >= this->glob.gl_pathc) { return FALSE; } match = this->glob.gl_pathv[this->pos++]; if (file) { *file = match; } if (st && stat(match, st)) { DBG1(DBG_LIB, "stat() on '%s' failed: %s", match, strerror(errno)); return FALSE; } return TRUE; } /* * Described in header */ enumerator_t* enumerator_create_glob(const char *pattern) { glob_enum_t *this; int status; if (!pattern) { return enumerator_create_empty(); } INIT(this, .public = { .enumerate = enumerator_enumerate_default, .venumerate = _enumerate_glob_enum, .destroy = _destroy_glob_enum, }, ); status = glob(pattern, GLOB_ERR, NULL, &this->glob); if (status == GLOB_NOMATCH) { DBG1(DBG_LIB, "no files found matching '%s'", pattern); } else if (status != 0) { DBG1(DBG_LIB, "expanding file pattern '%s' failed: %s", pattern, strerror(errno)); } return &this->public; } #else /* HAVE_GLOB_H */ enumerator_t* enumerator_create_glob(const char *pattern) { return NULL; } #endif /* HAVE_GLOB_H */ /** * Enumerator implementation for token enumerator */ typedef struct { /** implements enumerator_t */ enumerator_t public; /** string to parse */ char *string; /** current position */ char *pos; /** separator chars */ const char *sep; /** trim chars */ const char *trim; } token_enum_t; METHOD(enumerator_t, destroy_token_enum, void, token_enum_t *this) { free(this->string); free(this); } METHOD(enumerator_t, enumerate_token_enum, bool, token_enum_t *this, va_list args) { const char *sep, *trim; char *pos = NULL, *tmp, **token; bool last = FALSE; VA_ARGS_VGET(args, token); /* trim leading characters/separators */ while (*this->pos) { trim = this->trim; while (*trim) { if (*trim == *this->pos) { this->pos++; break; } trim++; } sep = this->sep; while (*sep) { if (*sep == *this->pos) { this->pos++; break; } sep++; } if (!*trim && !*sep) { break; } } switch (*this->pos) { case '"': case '\'': { /* read quoted token */ tmp = strchr(this->pos + 1, *this->pos); if (tmp) { *token = this->pos + 1; *tmp = '\0'; this->pos = tmp + 1; return TRUE; } /* unterminated string, FALL-THROUGH */ } default: { /* find nearest separator */ sep = this->sep; while (*sep) { tmp = strchr(this->pos, *sep); if (tmp && (pos == NULL || tmp < pos)) { pos = tmp; } sep++; } *token = this->pos; if (pos) { *pos = '\0'; this->pos = pos + 1; } else { last = TRUE; pos = this->pos = strchr(this->pos, '\0'); } break; } } /* trim trailing characters */ pos--; while (pos >= *token) { trim = this->trim; while (*trim) { if (*trim == *pos) { *(pos--) = '\0'; break; } trim++; } if (!*trim) { break; } } if (!last || pos >= *token) { return TRUE; } return FALSE; } /* * Described in header */ enumerator_t* enumerator_create_token(const char *string, const char *sep, const char *trim) { token_enum_t *this; INIT(this, .public = { .enumerate = enumerator_enumerate_default, .venumerate = _enumerate_token_enum, .destroy = _destroy_token_enum, }, .string = strdup(string), .sep = sep, .trim = trim, ); this->pos = this->string; return &this->public; } /** * Enumerator for nested enumerations */ typedef struct { enumerator_t public; enumerator_t *outer; enumerator_t *inner; enumerator_t *(*create_inner)(void *outer, void *data); void *data; void (*destructor)(void *data); } nested_enumerator_t; METHOD(enumerator_t, enumerate_nested, bool, nested_enumerator_t *this, va_list args) { while (TRUE) { while (!this->inner) { void *outer; if (!this->outer->enumerate(this->outer, &outer)) { return FALSE; } this->inner = this->create_inner(outer, this->data); if (this->inner && !this->inner->venumerate) { DBG1(DBG_LIB, "!!! ENUMERATE NESTED: venumerate() missing !!!"); return FALSE; } } if (this->inner->venumerate(this->inner, args)) { return TRUE; } this->inner->destroy(this->inner); this->inner = NULL; } } METHOD(enumerator_t, destroy_nested, void, nested_enumerator_t *this) { if (this->destructor) { this->destructor(this->data); } DESTROY_IF(this->inner); this->outer->destroy(this->outer); free(this); } /* * Described in header */ enumerator_t *enumerator_create_nested(enumerator_t *outer, enumerator_t *(inner_constructor)(void *outer, void *data), void *data, void (*destructor)(void *data)) { nested_enumerator_t *this; INIT(this, .public = { .enumerate = enumerator_enumerate_default, .venumerate = _enumerate_nested, .destroy = _destroy_nested, }, .outer = outer, .create_inner = inner_constructor, .data = data, .destructor = destructor, ); return &this->public; } /** * Enumerator for filtered enumerator */ typedef struct { enumerator_t public; enumerator_t *orig; void *data; bool (*filter)(void*,enumerator_t*,va_list); void (*destructor)(void *data); } filter_enumerator_t; METHOD(enumerator_t, destroy_filter, void, filter_enumerator_t *this) { if (this->destructor) { this->destructor(this->data); } this->orig->destroy(this->orig); free(this); } METHOD(enumerator_t, enumerate_filter, bool, filter_enumerator_t *this, va_list args) { bool result = FALSE; if (this->filter(this->data, this->orig, args)) { result = TRUE; } return result; } /* * Described in header */ enumerator_t *enumerator_create_filter(enumerator_t *orig, bool (*filter)(void *data, enumerator_t *orig, va_list args), void *data, void (*destructor)(void *data)) { filter_enumerator_t *this; INIT(this, .public = { .enumerate = enumerator_enumerate_default, .venumerate = _enumerate_filter, .destroy = _destroy_filter, }, .orig = orig, .filter = filter, .data = data, .destructor = destructor, ); return &this->public; } /** * Enumerator for cleaner enumerator */ typedef struct { enumerator_t public; enumerator_t *wrapped; void (*cleanup)(void *data); void *data; } cleaner_enumerator_t; METHOD(enumerator_t, destroy_cleaner, void, cleaner_enumerator_t *this) { this->cleanup(this->data); this->wrapped->destroy(this->wrapped); free(this); } METHOD(enumerator_t, enumerate_cleaner, bool, cleaner_enumerator_t *this, va_list args) { if (!this->wrapped->venumerate) { DBG1(DBG_LIB, "!!! CLEANER ENUMERATOR: venumerate() missing !!!"); return FALSE; } return this->wrapped->venumerate(this->wrapped, args); } /* * Described in header */ enumerator_t *enumerator_create_cleaner(enumerator_t *wrapped, void (*cleanup)(void *data), void *data) { cleaner_enumerator_t *this; INIT(this, .public = { .enumerate = enumerator_enumerate_default, .venumerate = _enumerate_cleaner, .destroy = _destroy_cleaner, }, .wrapped = wrapped, .cleanup = cleanup, .data = data, ); return &this->public; } /** * Enumerator for single enumerator */ typedef struct { enumerator_t public; void *item; void (*cleanup)(void *item); bool done; } single_enumerator_t; METHOD(enumerator_t, destroy_single, void, single_enumerator_t *this) { if (this->cleanup) { this->cleanup(this->item); } free(this); } METHOD(enumerator_t, enumerate_single, bool, single_enumerator_t *this, va_list args) { void **item; VA_ARGS_VGET(args, item); if (this->done) { return FALSE; } *item = this->item; this->done = TRUE; return TRUE; } /* * Described in header */ enumerator_t *enumerator_create_single(void *item, void (*cleanup)(void *item)) { single_enumerator_t *this; INIT(this, .public = { .enumerate = enumerator_enumerate_default, .venumerate = _enumerate_single, .destroy = _destroy_single, }, .item = item, .cleanup = cleanup, ); return &this->public; }