123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675 |
- /*
- * 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 <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * 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 <sys/types.h>
- #include <sys/stat.h>
- #include <unistd.h>
- #include <limits.h>
- #include <stdio.h>
- #include <dirent.h>
- #include <errno.h>
- #include <string.h>
- #ifdef HAVE_GLOB_H
- #include <glob.h>
- #endif /* HAVE_GLOB_H */
- #include <utils/debug.h>
- /*
- * 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;
- }
|