source: tags/ms_r16q2/ARBDB/adfile.cxx

Last change on this file was 14370, checked in by westram, 9 years ago
  • since [14054] updating WC to HEAD _always_ changed ARBDB ⇒ always caused _all_ unittests to run again
    • instead read revision from generated file (lib/revision_info.txt)
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.4 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : adfile.cxx                                        //
4//   Purpose   : various IO related functions                      //
5//                                                                 //
6//   Institute of Microbiology (Technical University Munich)       //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11#include <dirent.h>
12#include <unistd.h>
13#include <sys/stat.h>
14
15#include <arb_sort.h>
16#include <arb_str.h>
17#include <arb_strarray.h>
18#include <arb_file.h>
19#include <arb_pathlen.h>
20
21#include "gb_local.h"
22#include "gb_load.h"
23
24GB_CSTR GB_getcwd() {
25    // get the current working directory
26    // (directory from which application has been started)
27    RETURN_ONETIME_ALLOC(getcwd(0, ARB_PATH_MAX));
28}
29
30GB_ERROR gb_scan_directory(char *basename, gb_scandir *sd) {
31    // goes to header: __ATTR__USERESULT_TODO
32    // look for quick saves (basename = yyy/xxx no arb ending !!!!)
33    char        *path        = strdup(basename);
34    const char  *fulldir     = ".";
35    char        *file        = strrchr(path, '/');
36    DIR         *dirp;
37    int          curindex;
38    char        *suffix;
39    dirent      *dp;
40    struct stat  st;
41    const char  *oldstyle    = ".arb.quick";
42    char         buffer[ARB_PATH_MAX];
43    int          oldstylelen = strlen(oldstyle);
44    int          filelen;
45
46    if (file) {
47        *(file++) = 0;
48        fulldir = path;
49    }
50    else {
51        file = path;
52    }
53
54    memset((char*)sd, 0, sizeof(*sd));
55    sd->type = GB_SCAN_NO_QUICK;
56    sd->highest_quick_index = -1;
57    sd->newest_quick_index = -1;
58    sd->date_of_quick_file = 0;
59
60    dirp = opendir(fulldir);
61    if (!dirp) {
62        GB_ERROR error = GBS_global_string("Directory %s of file %s.arb not readable", fulldir, file);
63        free(path);
64        return error;
65    }
66    filelen = strlen(file);
67    for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
68        if (strncmp(dp->d_name, file, filelen)) continue;
69        suffix = dp->d_name + filelen;
70        if (suffix[0] != '.') continue;
71        if (!strncmp(suffix, oldstyle, oldstylelen)) {
72            if (sd->type == GB_SCAN_NEW_QUICK) {
73                printf("Warning: Found new and old changes files, using new\n");
74                continue;
75            }
76            sd->type = GB_SCAN_OLD_QUICK;
77            curindex = atoi(suffix+oldstylelen);
78            goto check_time_and_date;
79        }
80        if (strlen(suffix) == 4 &&
81            suffix[0] == '.' &&
82            suffix[1] == 'a' &&
83            isdigit(suffix[2]) &&
84            isdigit(suffix[3])) {
85            if (sd->type == GB_SCAN_OLD_QUICK) {
86                printf("Warning: Found new and old changes files, using new\n");
87            }
88            sd->type = GB_SCAN_NEW_QUICK;
89            curindex = atoi(suffix+2);
90            goto check_time_and_date;
91        }
92        continue;
93    check_time_and_date :
94        if (curindex > sd->highest_quick_index) sd->highest_quick_index = curindex;
95        sprintf(buffer, "%s/%s", fulldir, dp->d_name);
96        stat(buffer, &st);
97        if ((unsigned long)st.st_mtime > sd->date_of_quick_file) {
98            sd->date_of_quick_file = st.st_mtime;
99            sd->newest_quick_index = curindex;
100        }
101        continue;
102    }
103    closedir(dirp);
104    free(path);
105    return 0;
106}
107
108
109char *GB_find_all_files(const char *dir, const char *mask, bool filename_only) {
110    /* Returns a string containing the filenames of all files matching mask.
111       The single filenames are separated by '*'.
112       if 'filename_only' is true -> string contains only filenames w/o path
113
114       returns 0 if no files found (or directory not found).
115       in this case an error may be exported
116
117       'mask' may contain wildcards (*?) or
118       it may be a regular expression ('/regexp/')
119    */
120
121    DIR         *dirp;
122    struct stat  st;
123    char        *result = 0;
124
125    dirp = opendir(dir);
126    if (dirp) {
127        GBS_string_matcher *matcher = GBS_compile_matcher(mask, GB_IGNORE_CASE);
128        if (matcher) {
129            for (dirent *dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
130                if (GBS_string_matches_regexp(dp->d_name, matcher)) {
131                    char buffer[ARB_PATH_MAX];
132                    sprintf(buffer, "%s/%s", dir, dp->d_name);
133                    if (stat(buffer, &st) == 0  && S_ISREG(st.st_mode)) { // regular file ?
134                        if (filename_only) strcpy(buffer, dp->d_name);
135                        if (result) {
136                            freeset(result, GBS_global_string_copy("%s*%s", result, buffer));
137                        }
138                        else {
139                            result = strdup(buffer);
140                        }
141                    }
142                }
143            }
144            GBS_free_matcher(matcher);
145        }
146        closedir(dirp);
147    }
148
149    return result;
150}
151
152char *GB_find_latest_file(const char *dir, const char *mask) {
153    /* returns the name of the newest file in dir 'dir' matching 'mask'
154     * or NULL (in this case an error may be exported)
155     *
156     * 'mask' may contain wildcards (*?) or
157     * it may be a regular expression ('/regexp/')
158     */
159
160    DIR         *dirp;
161    struct stat  st;
162    char        *result = 0;
163
164    dirp = opendir(dir);
165    if (dirp) {
166        GBS_string_matcher *matcher = GBS_compile_matcher(mask, GB_IGNORE_CASE);
167        if (matcher) {
168            GB_ULONG newest = 0;
169            for (dirent *dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
170                if (GBS_string_matches_regexp(dp->d_name, matcher)) {
171                    char buffer[ARB_PATH_MAX];
172                    sprintf(buffer, "%s/%s", dir, dp->d_name);
173                    if (stat(buffer, &st) == 0 &&
174                        S_ISREG(st.st_mode) &&
175                        (GB_ULONG)st.st_mtime > newest) {
176                            newest = st.st_mtime;
177                            freedup(result, dp->d_name);
178                    }
179                }
180            }
181            GBS_free_matcher(matcher);
182        }
183        closedir(dirp);
184    }
185    return result;
186}
187
188static const char *GB_existing_file(const char *file, bool warn_when_not_found) {
189    // return 'file' if it's an existing readable file
190    // return NULL otherwise
191
192    gb_assert(file);
193    if (GB_is_readablefile(file)) return file;
194    if (warn_when_not_found) GB_warningf("Could not find '%s'", file);
195    return NULL;
196}
197
198char *GB_lib_file(bool warn_when_not_found, const char *libprefix, const char *filename) {
199    // Search a file in '$ARBHOME/lib/libprefix'
200    // Return NULL if not found
201    return nulldup(GB_existing_file(GB_path_in_ARBLIB(libprefix, filename), warn_when_not_found));
202}
203
204char *GB_property_file(bool warn_when_not_found, const char *filename) {
205    // Search a file in '$ARB_PROP' or its default in '$ARBHOME/lib/arb_default'
206    // Return NULL if neighter found
207
208    char *result = nulldup(GB_existing_file(GB_path_in_arbprop(filename), warn_when_not_found));
209    if (!result) result = GB_lib_file(warn_when_not_found, "arb_default", filename);
210    return result;
211}
212
213void GBS_read_dir(StrArray& names, const char *dir, const char *mask) {
214    /* Return full pathnames of files in directory 'dir'.
215     *
216     * Filter through 'mask':
217     * - mask == NULL -> return all files
218     * -      in format '/expr/' -> use regular expression (case sensitive)
219     * - else it does a simple string match with wildcards ("?*")
220     *
221     * Result are inserted into 'names' and 'names' is sorted alphanumerically.
222     * Note: 'names' are not cleared, so several calls with the same StrArray get collected.
223     *
224     * In case of error, 'names' is empty and error is exported.
225     *
226     * Special case: If 'dir' is the name of a file, return an array with file as only element
227     */
228
229    gb_assert(dir);             // dir == NULL was allowed before 12/2008, forbidden now!
230
231    if (dir[0]) {
232        char *fulldir   = strdup(GB_canonical_path(dir));
233        DIR  *dirstream = opendir(fulldir);
234
235        if (!dirstream) {
236            if (GB_is_readablefile(fulldir)) { // fixed: returned true for directories before (4/2012)
237                names.put(strdup(fulldir));
238            }
239            else {
240                // @@@ does too much voodoo here - fix
241                char *lslash = strrchr(fulldir, '/');
242
243                if (lslash) {
244                    lslash[0]  = 0;
245                    char *name = lslash+1;
246                    if (GB_is_directory(fulldir)) {
247                        GBS_read_dir(names, fulldir, name); // @@@ concat mask to name?
248                    }
249                    lslash[0] = '/';
250                }
251
252                if (names.empty()) GB_export_errorf("can't read directory '%s'", fulldir); // @@@ wrong place for error; maybe return error as result
253            }
254        }
255        else {
256            if (mask == NULL) mask = "*";
257
258            GBS_string_matcher *matcher = GBS_compile_matcher(mask, GB_MIND_CASE);
259            if (matcher) {
260                dirent *entry;
261                while ((entry = readdir(dirstream)) != 0) {
262                    const char *name = entry->d_name;
263
264                    if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
265                        ; // skip '.' and '..'
266                    }
267                    else {
268                        if (GBS_string_matches_regexp(name, matcher)) {
269                            const char *full = GB_concat_path(fulldir, name);
270                            if (!GB_is_directory(full)) { // skip directories
271                                names.put(strdup(full));
272                            }
273                        }
274                    }
275                }
276
277                names.sort(GB_string_comparator, 0);
278
279                GBS_free_matcher(matcher);
280            }
281
282            closedir(dirstream);
283        }
284
285        free(fulldir);
286    }
287}
288
289const char *GB_get_arb_revision_tag() {
290    /*! retrieve arb revision tag (e.g. "14358@trunk") from ../lib/revision_info.txt
291     * File is generated by ../SOURCE_TOOLS/build_info.pl
292     */
293    static SmartCharPtr tag;
294
295    if (tag.isNull()) {
296        char *name = GB_lib_file(true, NULL, "revision_info.txt");
297        tag        = GB_read_file(name);
298
299        char *nl    = strchr(&*tag, '\n');
300        if (nl) *nl = 0;
301
302        if (tag.isNull()) tag = GBS_global_string_copy("<failed to read %s>", name);
303        free(name);
304    }
305    return &*tag;
306}
307
308
309// --------------------------------------------------------------------------------
310
311#ifdef UNIT_TESTS
312#include <test_unit.h>
313
314static char *remove_path(const char *fullname, void *cl_path) {
315    const char *path = (const char *)cl_path;
316    return strdup(fullname+(ARB_strBeginsWith(fullname, path) ? strlen(path) : 0));
317}
318
319static void GBT_transform_names(StrArray& dest, const StrArray& source, char *transform(const char *, void *), void *client_data) {
320    for (int i = 0; source[i]; ++i) dest.put(transform(source[i], client_data));
321}
322
323#define TEST_JOINED_FULLDIR_CONTENT_EQUALS(fulldir,mask,expected) do {                  \
324        StrArray  contents;                                                             \
325        GBS_read_dir(contents, fulldir, mask);                                          \
326        StrArray  contents_no_path;                                                     \
327        GBT_transform_names(contents_no_path, contents, remove_path, (void*)fulldir);   \
328        char     *joined  = GBT_join_strings(contents_no_path, '!');                    \
329        TEST_EXPECT_EQUAL(joined, expected);                                            \
330        free(joined);                                                                   \
331    } while(0)
332
333#define TEST_JOINED_DIR_CONTENT_EQUALS(subdir,mask,expected) do {       \
334        char     *fulldir = strdup(GB_path_in_ARBHOME(subdir));         \
335        TEST_JOINED_FULLDIR_CONTENT_EQUALS(fulldir,mask,expected);      \
336        free(fulldir);                                                  \
337    } while(0)
338
339void TEST_GBS_read_dir() {
340    TEST_JOINED_DIR_CONTENT_EQUALS("GDE/CLUSTAL", "*.c",       "/amenu.c!/clustalv.c!/gcgcheck.c!/myers.c!/sequence.c!/showpair.c!/trees.c!/upgma.c!/util.c");
341    TEST_JOINED_DIR_CONTENT_EQUALS("GDE/CLUSTAL", "/s.*\\.c/", "/clustalv.c!/myers.c!/sequence.c!/showpair.c!/trees.c");
342
343    // test a dir containing subdirectories
344    TEST_JOINED_DIR_CONTENT_EQUALS("SL", NULL, "/Makefile!/README");
345    TEST_JOINED_DIR_CONTENT_EQUALS("SL", "*",  "/Makefile!/README");
346
347    TEST_JOINED_FULLDIR_CONTENT_EQUALS("", "", ""); // allow GBS_read_dir to be called with "" -> returns empty filelist
348}
349
350void TEST_find_file() {
351    gb_getenv_hook old = GB_install_getenv_hook(arb_test::fakeenv);
352
353    TEST_EXPECT_EQUAL(GB_existing_file("min_ascii.arb", false), "min_ascii.arb");
354    TEST_EXPECT_NULL(GB_existing_file("nosuchfile", false));
355
356    char *tcporg = GB_lib_file(false, "", "arb_tcp_org.dat");
357    TEST_EXPECT_EQUAL(tcporg, GB_path_in_ARBHOME("lib/arb_tcp_org.dat"));
358    TEST_EXPECT_NULL(GB_lib_file(true, "bla", "blub"));
359    free(tcporg);
360
361    char *status = GB_property_file(false, "status.arb");
362    TEST_EXPECT_EQUAL(status, GB_path_in_ARBHOME("lib/arb_default/status.arb"));
363    TEST_EXPECT_NULL(GB_property_file(true, "undhepp"));
364    free(status);
365
366    TEST_EXPECT_EQUAL((void*)arb_test::fakeenv, (void*)GB_install_getenv_hook(old));
367}
368TEST_PUBLISH(TEST_find_file);
369
370#endif // UNIT_TESTS
Note: See TracBrowser for help on using the repository browser.