From de82e7fd43191fc2ae0d8eab9781c59c595c343a Mon Sep 17 00:00:00 2001 From: Adam Dickmeiss Date: Tue, 26 Jan 2010 12:19:47 +0100 Subject: [PATCH] File globbing functional --- include/yaz/Makefile.am | 2 +- include/yaz/file_glob.h | 88 ++++++++++++++++++++++++++++++++++++++ src/file_glob.c | 108 ++++++++++++++++++++++++++++++++++++++++++++--- test/Makefile.am | 3 +- test/tst_file_glob.c | 84 ++++++++++++++++++++++++++++++++++++ 5 files changed, 277 insertions(+), 8 deletions(-) create mode 100644 include/yaz/file_glob.h create mode 100644 test/tst_file_glob.c diff --git a/include/yaz/Makefile.am b/include/yaz/Makefile.am index d40777d..d580317 100644 --- a/include/yaz/Makefile.am +++ b/include/yaz/Makefile.am @@ -19,7 +19,7 @@ pkginclude_HEADERS= backend.h ccl.h ccl_xml.h cql.h rpn2cql.h comstack.h \ z-grs.h z-mterm2.h z-opac.h z-rrf1.h z-rrf2.h z-sum.h z-sutrs.h z-uifr1.h \ z-univ.h z-oclcui.h zes-expi.h zes-exps.h zes-order.h zes-pquery.h \ zes-psched.h zes-admin.h zes-pset.h zes-update.h zes-update0.h \ - zoom.h z-charneg.h charneg.h soap.h srw.h zgdu.h matchstr.h json.h + zoom.h z-charneg.h charneg.h soap.h srw.h zgdu.h matchstr.h json.h file_glob.h EXTRA_DIST = yaz-version.h.in diff --git a/include/yaz/file_glob.h b/include/yaz/file_glob.h new file mode 100644 index 0000000..dc0b45c --- /dev/null +++ b/include/yaz/file_glob.h @@ -0,0 +1,88 @@ +/* This file is part of the YAZ toolkit. + * Copyright (C) 1995-2010 Index Data. + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Index Data nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** \file + \brief File globbing (ala POSIX glob, but simpler) +*/ + +#ifndef YAZ_FILE_GLOB_H +#define YAZ_FILE_GLOB_H + +#include +#include + +YAZ_BEGIN_CDECL + +/** \brief file glob handle */ +typedef struct glob_res *yaz_glob_res_t; + + +/** \brief perform glob + \param pattern glob pattern file spec + \param res returned glob result + \retval 0 OK + \retval -1 ERROR +*/ +YAZ_EXPORT +int yaz_file_glob(const char *pattern, yaz_glob_res_t *res); + +/** \brief release glob result + \param res pointer to glob result + + A value of *res == NULL is allowed. If *res != NULL, then + *res is set to NULL + */ +YAZ_EXPORT +void yaz_file_globfree(yaz_glob_res_t *res); + +/** \brief return resulting matching file + \param res glob result + \param idx index 0=first, .. N-1 (where N is yaz_file_glob_get_num) + \returns file name or NULL if idx is out-of-range +*/ +YAZ_EXPORT +const char *yaz_file_glob_get_file(yaz_glob_res_t res, size_t idx); + +/** \brief return number of matching files + \param res glob result + \returns number of files +*/ +YAZ_EXPORT +size_t yaz_file_glob_get_num(yaz_glob_res_t res); + +YAZ_END_CDECL + +#endif +/* + * Local variables: + * c-basic-offset: 4 + * c-file-style: "Stroustrup" + * indent-tabs-mode: nil + * End: + * vim: shiftwidth=4 tabstop=8 expandtab + */ + diff --git a/src/file_glob.c b/src/file_glob.c index 2f3ff7f..8b10664 100644 --- a/src/file_glob.c +++ b/src/file_glob.c @@ -4,7 +4,7 @@ */ /** \file - \brief File globbing (ala POSIX glob) + \brief File globbing (ala POSIX glob, but simpler) */ #if HAVE_CONFIG_H #include @@ -18,32 +18,128 @@ #include #include #include +#include +#include +#include +#include struct res_entry { struct res_entry *next; + char *file; }; struct glob_res { + NMEM nmem; + size_t number_of_entries; + struct res_entry **last_entry; struct res_entry *entries; }; -typedef struct glob_res *yaz_glob_res_t; - -static void glob_r(const char *pattern, yaz_glob_res_t res, size_t off) +static void glob_r(yaz_glob_res_t res, const char *pattern, size_t off, + char *prefix) { + size_t prefix_len = strlen(prefix); + int is_pattern = 0; size_t i = off; while (pattern[i] && !strchr("/\\", pattern[i])) + { + if (strchr("?*", pattern[i])) + is_pattern = 1; i++; + } + + if (!is_pattern && pattern[i]) /* no pattern and directory part */ + { + i++; /* skip dir sep */ + memcpy(prefix + prefix_len, pattern + off, i - off); + prefix[prefix_len + i - off] = '\0'; + glob_r(res, pattern, i, prefix); + prefix[prefix_len] = '\0'; + } + else + { + DIR * dir = opendir(*prefix ? prefix : "." ); + + if (dir) + { + struct dirent *ent; + + while ((ent = readdir(dir))) + { + int r; + memcpy(prefix + prefix_len, pattern + off, i - off); + prefix[prefix_len + i - off] = '\0'; + r = yaz_match_glob(prefix + prefix_len, ent->d_name); + prefix[prefix_len] = '\0'; + + if (r) + { + strcpy(prefix + prefix_len, ent->d_name); + if (pattern[i]) + { + glob_r(res, pattern, i, prefix); + } + else + { + struct res_entry *ent = + nmem_malloc(res->nmem, sizeof(*ent)); + ent->file = nmem_strdup(res->nmem, prefix); + ent->next = 0; + *res->last_entry = ent; + res->last_entry = &ent->next; + res->number_of_entries++; + } + prefix[prefix_len] = '\0'; + } + } + closedir(dir); + } + } } int yaz_file_glob(const char *pattern, yaz_glob_res_t *res) { - *res = xmalloc(sizeof(**res)); + char prefix[FILENAME_MAX+1]; + NMEM nmem = nmem_create(); + + *prefix = '\0'; + *res = nmem_malloc(nmem, sizeof(**res)); + (*res)->number_of_entries = 0; + (*res)->nmem = nmem; (*res)->entries = 0; - glob_r(pattern, *res, 0); + (*res)->last_entry = &(*res)->entries; + glob_r(*res, pattern, 0, prefix); return 0; } +void yaz_file_globfree(yaz_glob_res_t *res) +{ + if (*res) + { + /* must free entries as well */ + nmem_destroy((*res)->nmem); + *res = 0; + } +} + +const char *yaz_file_glob_get_file(yaz_glob_res_t res, size_t idx) +{ + struct res_entry *ent = res->entries; + while (idx && ent) + { + ent = ent->next; + idx--; + } + if (!ent) + return 0; + return ent->file; +} + +size_t yaz_file_glob_get_num(yaz_glob_res_t res) +{ + return res->number_of_entries; +} + /* * Local variables: * c-basic-offset: 4 diff --git a/test/Makefile.am b/test/Makefile.am index e813f65..35f819c 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -6,7 +6,7 @@ check_PROGRAMS = tstxmalloc tsticonv tstnmem tstmatchstr tstwrbuf tstodr \ tstsoap1 tstsoap2 tstodrstack tstlogthread tstxmlquery tstpquery \ tst_comstack tst_filepath tst_record_conv tst_retrieval tst_tpath \ tst_timing tst_query_charset tst_oid tst_icu_I18N tst_match_glob \ - tst_rpn2cql tst_json tst_xml_include + tst_rpn2cql tst_json tst_xml_include tst_file_glob check_SCRIPTS = tstmarc.sh tstmarccol.sh tstcql2xcql.sh tstcql2pqf.sh tsticu.sh @@ -80,3 +80,4 @@ tst_match_glob_SOURCES = tst_match_glob.c tst_rpn2cql_SOURCES = tst_rpn2cql.c tst_json_SOURCES = tst_json.c tst_xml_include_SOURCES = tst_xml_include.c +tst_file_glob_SOURCES = tst_file_glob.c diff --git a/test/tst_file_glob.c b/test/tst_file_glob.c new file mode 100644 index 0000000..8cfc3c3 --- /dev/null +++ b/test/tst_file_glob.c @@ -0,0 +1,84 @@ +/* This file is part of the YAZ toolkit. + * Copyright (C) 1995-2010 Index Data + * See the file LICENSE for details. + */ + +#if HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include + +void tst_with_path(const char *tpath) +{ + yaz_glob_res_t glob_res; + int ret = yaz_file_glob(tpath, &glob_res); + if (ret == 0) + { + size_t n = yaz_file_glob_get_num(glob_res); + size_t i; + for (i = 0; i < n; i++) + { + yaz_log(YLOG_LOG, "match %s", yaz_file_glob_get_file(glob_res, i)); + } + } + yaz_file_globfree(&glob_res); +} + +void tst(void) +{ + yaz_glob_res_t glob_res; + int ret; + WRBUF tpath = wrbuf_alloc(); + const char *srcdir = getenv("srcdir"); + + if (srcdir) + { + wrbuf_puts(tpath, srcdir); + wrbuf_puts(tpath, "/"); + } + wrbuf_puts(tpath, "Make*.am"); + ret = yaz_file_glob(wrbuf_cstr(tpath), &glob_res); + YAZ_CHECK_EQ(ret, 0); + + YAZ_CHECK_EQ(1, yaz_file_glob_get_num(glob_res)); + if (yaz_file_glob_get_num(glob_res) == 1) + { + const char *f = yaz_file_glob_get_file(glob_res, 0); + size_t l_match = strlen("Makefile.am"); + YAZ_CHECK(f && strlen(f) >= l_match); + if (f && strlen(f) >= l_match) + { + YAZ_CHECK(!strcmp(f + strlen(f) - l_match, "Makefile.am")); + } + } + wrbuf_destroy(tpath); + yaz_file_globfree(&glob_res); +} + +int main (int argc, char **argv) +{ + YAZ_CHECK_INIT(argc, argv); + if (argc >= 2) + tst_with_path(argv[1]); + else + tst(); + YAZ_CHECK_TERM; +} +/* + * Local variables: + * c-basic-offset: 4 + * c-file-style: "Stroustrup" + * indent-tabs-mode: nil + * End: + * vim: shiftwidth=4 tabstop=8 expandtab + */ + -- 1.7.10.4