source: tags/ms_r17q3/ARBDB/adfile.cxx

Last change on this file was 15176, checked in by westram, 8 years ago
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.5 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        = ARB_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 = ARB_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   = ARB_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(fulldir); // transfer ownership
238                fulldir = NULL;
239            }
240            else {
241                // @@@ does too much voodoo here - fix
242                char *lslash = strrchr(fulldir, '/');
243
244                if (lslash) {
245                    lslash[0]  = 0;
246                    char *name = lslash+1;
247                    if (GB_is_directory(fulldir)) {
248                        GBS_read_dir(names, fulldir, name); // @@@ concat mask to name?
249                    }
250                    lslash[0] = '/';
251                }
252
253                if (names.empty()) GB_export_errorf("can't read directory '%s'", fulldir); // @@@ wrong place for error; maybe return error as result
254            }
255        }
256        else {
257            if (mask == NULL) mask = "*";
258
259            GBS_string_matcher *matcher = GBS_compile_matcher(mask, GB_MIND_CASE);
260            if (matcher) {
261                dirent *entry;
262                while ((entry = readdir(dirstream)) != 0) {
263                    const char *name = entry->d_name;
264
265                    if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
266                        ; // skip '.' and '..'
267                    }
268                    else {
269                        if (GBS_string_matches_regexp(name, matcher)) {
270                            const char *full = GB_concat_path(fulldir, name);
271                            if (!GB_is_directory(full)) { // skip directories
272                                names.put(ARB_strdup(full));
273                            }
274                        }
275                    }
276                }
277
278                names.sort(GB_string_comparator, 0);
279
280                GBS_free_matcher(matcher);
281            }
282
283            closedir(dirstream);
284        }
285
286        free(fulldir);
287    }
288}
289
290const char *GB_get_arb_revision_tag() {
291    /*! retrieve arb revision tag (e.g. "14358@trunk") from ../lib/revision_info.txt
292     * File is generated by ../SOURCE_TOOLS/build_info.pl
293     */
294    static SmartCharPtr tag;
295
296    if (tag.isNull()) {
297        char *name = GB_lib_file(true, NULL, "revision_info.txt");
298        tag        = GB_read_file(name);
299
300        char *nl    = strchr(&*tag, '\n');
301        if (nl) *nl = 0;
302
303        if (tag.isNull()) tag = GBS_global_string_copy("<failed to read %s>", name);
304        free(name);
305    }
306    return &*tag;
307}
308
309
310// --------------------------------------------------------------------------------
311
312#ifdef UNIT_TESTS
313#include <test_unit.h>
314
315static char *remove_path(const char *fullname, void *cl_path) {
316    const char *path = (const char *)cl_path;
317    return ARB_strdup(fullname+(ARB_strBeginsWith(fullname, path) ? strlen(path) : 0));
318}
319
320static void GBT_transform_names(StrArray& dest, const StrArray& source, char *transform(const char *, void *), void *client_data) {
321    for (int i = 0; source[i]; ++i) dest.put(transform(source[i], client_data));
322}
323
324#define TEST_JOINED_FULLDIR_CONTENT_EQUALS(fulldir,mask,expected) do {                  \
325        StrArray  contents;                                                             \
326        GBS_read_dir(contents, fulldir, mask);                                          \
327        StrArray  contents_no_path;                                                     \
328        GBT_transform_names(contents_no_path, contents, remove_path, (void*)fulldir);   \
329        char     *joined  = GBT_join_strings(contents_no_path, '!');                    \
330        TEST_EXPECT_EQUAL(joined, expected);                                            \
331        free(joined);                                                                   \
332    } while(0)
333
334#define TEST_JOINED_DIR_CONTENT_EQUALS(subdir,mask,expected) do {       \
335        char     *fulldir = ARB_strdup(GB_path_in_ARBHOME(subdir));     \
336        TEST_JOINED_FULLDIR_CONTENT_EQUALS(fulldir,mask,expected);      \
337        free(fulldir);                                                  \
338    } while(0)
339
340void TEST_GBS_read_dir() {
341    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");
342    TEST_JOINED_DIR_CONTENT_EQUALS("GDE/CLUSTAL", "/s.*\\.c/", "/clustalv.c!/myers.c!/sequence.c!/showpair.c!/trees.c");
343
344    // test a dir containing subdirectories
345    TEST_JOINED_DIR_CONTENT_EQUALS("SL", NULL, "/Makefile!/README");
346    TEST_JOINED_DIR_CONTENT_EQUALS("SL", "*",  "/Makefile!/README");
347
348    TEST_JOINED_FULLDIR_CONTENT_EQUALS("", "", ""); // allow GBS_read_dir to be called with "" -> returns empty filelist
349}
350
351void TEST_find_file() {
352    gb_getenv_hook old = GB_install_getenv_hook(arb_test::fakeenv);
353
354    TEST_EXPECT_EQUAL(GB_existing_file("min_ascii.arb", false), "min_ascii.arb");
355    TEST_EXPECT_NULL(GB_existing_file("nosuchfile", false));
356
357    char *tcporg = GB_lib_file(false, "", "arb_tcp_org.dat");
358    TEST_EXPECT_EQUAL(tcporg, GB_path_in_ARBHOME("lib/arb_tcp_org.dat"));
359    TEST_EXPECT_NULL(GB_lib_file(true, "bla", "blub"));
360    free(tcporg);
361
362    char *status = GB_property_file(false, "status.arb");
363    TEST_EXPECT_EQUAL(status, GB_path_in_ARBHOME("lib/arb_default/status.arb"));
364    TEST_EXPECT_NULL(GB_property_file(true, "undhepp"));
365    free(status);
366
367    TEST_EXPECT_EQUAL((void*)arb_test::fakeenv, (void*)GB_install_getenv_hook(old));
368}
369TEST_PUBLISH(TEST_find_file);
370
371#endif // UNIT_TESTS
Note: See TracBrowser for help on using the repository browser.