root/trunk/ARBDB/adfile.cxx

Revision 8649, 26.5 KB (checked in by westram, 5 weeks ago)
  • GB_is_readablefile no longer returns true for directories
    • affects behavior of fileboxes
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
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 <climits>
12#include <cctype>
13
14#include <dirent.h>
15#include <unistd.h>
16#include <sys/stat.h>
17
18#include <list>
19#include <string>
20
21#include <arb_sort.h>
22#include <arb_str.h>
23#include <arb_strarray.h>
24#include <arb_file.h>
25
26#include "gb_local.h"
27#include "gb_load.h"
28
29#define FILE_PATH_MAX PATH_MAX
30
31GB_CSTR GB_getcwd() {
32    // get the current working directory
33    // (directory from which application has been started)
34    RETURN_ONETIME_ALLOC(getcwd(0, FILE_PATH_MAX));
35}
36
37GB_ERROR gb_scan_directory(char *basename, gb_scandir *sd) {
38    // goes to header: __ATTR__USERESULT_TODO
39    // look for quick saves (basename = yyy/xxx no arb ending !!!!)
40    char        *path        = strdup(basename);
41    const char  *fulldir     = ".";
42    char        *file        = strrchr(path, '/');
43    DIR         *dirp;
44    int          curindex;
45    char        *suffix;
46    dirent      *dp;
47    struct stat  st;
48    const char  *oldstyle    = ".arb.quick";
49    char         buffer[FILE_PATH_MAX];
50    int          oldstylelen = strlen(oldstyle);
51    int          filelen;
52
53    if (file) {
54        *(file++) = 0;
55        fulldir = path;
56    }
57    else {
58        file = path;
59    }
60
61    memset((char*)sd, 0, sizeof(*sd));
62    sd->type = GB_SCAN_NO_QUICK;
63    sd->highest_quick_index = -1;
64    sd->newest_quick_index = -1;
65    sd->date_of_quick_file = 0;
66
67    dirp = opendir(fulldir);
68    if (!dirp) {
69        GB_ERROR error = GBS_global_string("Directory %s of file %s.arb not readable", fulldir, file);
70        free(path);
71        return error;
72    }
73    filelen = strlen(file);
74    for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
75        if (strncmp(dp->d_name, file, filelen)) continue;
76        suffix = dp->d_name + filelen;
77        if (suffix[0] != '.') continue;
78        if (!strncmp(suffix, oldstyle, oldstylelen)) {
79            if (sd->type == GB_SCAN_NEW_QUICK) {
80                printf("Warning: Found new and old changes files, using new\n");
81                continue;
82            }
83            sd->type = GB_SCAN_OLD_QUICK;
84            curindex = atoi(suffix+oldstylelen);
85            goto check_time_and_date;
86        }
87        if (strlen(suffix) == 4 &&
88            suffix[0] == '.' &&
89            suffix[1] == 'a' &&
90            isdigit(suffix[2]) &&
91            isdigit(suffix[3])) {
92            if (sd->type == GB_SCAN_OLD_QUICK) {
93                printf("Warning: Found new and old changes files, using new\n");
94            }
95            sd->type = GB_SCAN_NEW_QUICK;
96            curindex = atoi(suffix+2);
97            goto check_time_and_date;
98        }
99        continue;
100    check_time_and_date :
101        if (curindex > sd->highest_quick_index) sd->highest_quick_index = curindex;
102        sprintf(buffer, "%s/%s", fulldir, dp->d_name);
103        stat(buffer, &st);
104        if ((unsigned long)st.st_mtime > sd->date_of_quick_file) {
105            sd->date_of_quick_file = st.st_mtime;
106            sd->newest_quick_index = curindex;
107        }
108        continue;
109    }
110    closedir(dirp);
111    free(path);
112    return 0;
113}
114
115
116char *GB_find_all_files(const char *dir, const char *mask, bool filename_only) {
117    /* Returns a string containing the filenames of all files matching mask.
118       The single filenames are separated by '*'.
119       if 'filename_only' is true -> string contains only filenames w/o path
120
121       returns 0 if no files found (or directory not found).
122       in this case an error may be exported
123
124       'mask' may contain wildcards (*?) or
125       it may be a regular expression ('/regexp/')
126    */
127
128    DIR         *dirp;
129    dirent      *dp;
130    struct stat  st;
131    char        *result = 0;
132    char         buffer[FILE_PATH_MAX];
133
134    dirp = opendir(dir);
135    if (dirp) {
136        GBS_string_matcher *matcher = GBS_compile_matcher(mask, GB_IGNORE_CASE);
137        if (matcher) {
138            for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
139                if (GBS_string_matches_regexp(dp->d_name, matcher)) {
140                    sprintf(buffer, "%s/%s", dir, dp->d_name);
141                    if (stat(buffer, &st) == 0  && S_ISREG(st.st_mode)) { // regular file ?
142                        if (filename_only) strcpy(buffer, dp->d_name);
143                        if (result) {
144                            freeset(result, GBS_global_string_copy("%s*%s", result, buffer));
145                        }
146                        else {
147                            result = strdup(buffer);
148                        }
149                    }
150                }
151            }
152            GBS_free_matcher(matcher);
153        }
154        closedir(dirp);
155    }
156
157    return result;
158}
159
160char *GB_find_latest_file(const char *dir, const char *mask) {
161    /* returns the name of the newest file in dir 'dir' matching 'mask'
162     * or NULL (in this case an error may be exported)
163     *
164     * 'mask' may contain wildcards (*?) or
165     * it may be a regular expression ('/regexp/')
166     */
167
168    DIR         *dirp;
169    dirent      *dp;
170    char         buffer[FILE_PATH_MAX];
171    struct stat  st;
172    GB_ULONG     newest = 0;
173    char        *result = 0;
174
175    dirp = opendir(dir);
176    if (dirp) {
177        GBS_string_matcher *matcher = GBS_compile_matcher(mask, GB_IGNORE_CASE);
178        if (matcher) {
179            for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
180                if (GBS_string_matches_regexp(dp->d_name, matcher)) {
181                    sprintf(buffer, "%s/%s", dir, dp->d_name);
182                    if (stat(buffer, &st) == 0) {
183                        if ((GB_ULONG)st.st_mtime > newest) {
184                            newest = st.st_mtime;
185                            freedup(result, dp->d_name);
186                        }
187                    }
188                }
189            }
190            GBS_free_matcher(matcher);
191        }
192        closedir(dirp);
193    }
194    return result;
195}
196
197static const char *GB_existing_file(const char *file, bool warn_when_not_found) {
198    // return 'file' if it's an existing readable file
199    // return NULL otherwise
200
201    gb_assert(file);
202    if (GB_is_readablefile(file)) return file;
203    if (warn_when_not_found) GB_warningf("Could not find '%s'", file);
204    return NULL;
205}
206
207char *GB_lib_file(bool warn_when_not_found, const char *libprefix, const char *filename) {
208    // Search a file in '$ARBHOME/lib/libprefix'
209    // Return NULL if not found
210    return nulldup(GB_existing_file(GB_path_in_ARBLIB(libprefix, filename), warn_when_not_found));
211}
212
213char *GB_property_file(bool warn_when_not_found, const char *filename) {
214    // Search a file in '~/.arb_prop' or its default in '$ARBHOME/lib/arb_default'
215    // Return NULL if neighter found
216
217    char *result = nulldup(GB_existing_file(GB_path_in_arbprop(filename), warn_when_not_found));
218    if (!result) result = GB_lib_file(warn_when_not_found, "arb_default", filename);
219    return result;
220}
221
222void GBS_read_dir(StrArray& names, const char *dir, const char *mask) {
223    /* Return names of files in directory 'dir'.
224     * Filter through 'mask':
225     * - mask == NULL -> return all files
226     * -      in format '/expr/' -> use regular expression (case sensitive)
227     * - else it does a simple string match with wildcards ("?*")
228     *
229     * Result are inserted into 'names' and 'names' is sorted alphanumerically.
230     * Note: 'names' are not cleared, so several calls with the same StrArray get collected.
231     *
232     * In case of error, result is empty and error is exported.
233     *
234     * Special case: If 'dir' is the name of a file, return an array with file as only element
235     */
236
237    gb_assert(dir);             // dir == NULL was allowed before 12/2008, forbidden now!
238
239    char *fulldir   = strdup(GB_canonical_path(dir));
240    DIR  *dirstream = opendir(fulldir);
241
242    if (!dirstream) {
243        if (GB_is_readablefile(fulldir)) { // fixed: returned true for directories before (4/2012)
244            names.put(strdup(fulldir));
245        }
246        else {
247            // @@@ does too much voodoo here - fix
248            char *lslash = strrchr(fulldir, '/');
249
250            if (lslash) {
251                lslash[0]  = 0;
252                char *name = lslash+1;
253                if (GB_is_directory(fulldir)) {
254                    GBS_read_dir(names, fulldir, name); // @@@ concat mask to name?
255                }
256                lslash[0] = '/';
257            }
258
259            if (names.empty()) GB_export_errorf("can't read directory '%s'", fulldir); // @@@ wrong place for error; maybe return error as result
260        }
261    }
262    else {
263        if (mask == NULL) mask = "*";
264
265        GBS_string_matcher *matcher = GBS_compile_matcher(mask, GB_MIND_CASE);
266        if (matcher) {
267            dirent *entry;
268            while ((entry = readdir(dirstream)) != 0) {
269                const char *name = entry->d_name;
270
271                if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
272                    ; // skip '.' and '..'
273                }
274                else {
275                    if (GBS_string_matches_regexp(name, matcher)) {
276                        const char *full = GB_concat_path(fulldir, name);
277                        if (!GB_is_directory(full)) { // skip directories
278                            names.put(strdup(full));
279                        }
280                    }
281                }
282            }
283
284            names.sort(GB_string_comparator, 0);
285
286            GBS_free_matcher(matcher);
287        }
288
289        closedir(dirstream);
290    }
291
292    free(fulldir);
293}
294
295// --------------------------------------------------------------------------------
296
297#ifdef UNIT_TESTS
298#include <test_unit.h>
299
300// GB_test_textfile_difflines + GB_test_files_equal are helper functions used
301// by unit tests.
302//
303// @@@ GB_test_textfile_difflines + GB_test_files_equal -> ARBCORE
304
305#define MAX_REGS 13
306
307class difflineMode : virtual Noncopyable {
308    int               mode;
309    GBS_regex        *reg[MAX_REGS];
310    const char       *replace[MAX_REGS];
311    int               count;
312    mutable GB_ERROR  error;
313
314    static bool is_may;
315
316    void add(const char *regEx, GB_CASE case_flag, const char *replacement) {
317        if (!error) {
318            gb_assert(count<MAX_REGS);
319            reg[count] = GBS_compile_regexpr(regEx, case_flag, &error);
320            if (!error) {
321                replace[count] = replacement;
322                count++;
323            }
324        }
325    }
326
327public:
328    difflineMode(int mode_)
329        : mode(mode_),
330          count(0),
331          error(NULL)
332    {
333        memset(reg, 0, sizeof(reg));
334        switch (mode) {
335            case 0: break;
336            case 1:  {
337                add("[0-9]{2}:[0-9]{2}:[0-9]{2}", GB_MIND_CASE, "<TIME>");
338                add("(Mon|Tue|Wed|Thu|Fri|Sat|Sun)", GB_IGNORE_CASE, "<DOW>");
339
340                add("(January|February|March|April|May|June|July|August|September|October|November|December)", GB_IGNORE_CASE, "<Month>");
341                add("(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)", GB_IGNORE_CASE, "<MON>");
342
343                add("<MON>[ -][0-9]{4}",   GB_IGNORE_CASE, "<MON> <YEAR>");
344                add("<Month>[ -][0-9]{4}", GB_IGNORE_CASE, "<Month> <YEAR>");
345
346                add("<TIME>[ -][0-9]{4}",  GB_IGNORE_CASE, "<TIME> <YEAR>");
347
348                add("<MON>[ -][0-9 ]?[0-9]",   GB_IGNORE_CASE, "<MON> <DAY>");
349                add("<Month>[ -][0-9 ]?[0-9]", GB_IGNORE_CASE, "<Month> <DAY>");
350
351                add("[0-9]{2}[ -]<MON>",   GB_IGNORE_CASE, "<DAY> <MON>");
352                add("[0-9]{2}[ -]<Month>", GB_IGNORE_CASE, "<DAY> <Month>");
353
354                add("<DAY>, [0-9]{4}", GB_IGNORE_CASE, "<DAY> <YEAR>");
355
356                if (is_may) {
357                    // 'May' does not differ between short/long monthname
358                    // -> use less accurate tests in May
359                    add("<Month>", GB_MIND_CASE, "<MON>");
360                }
361
362                break;
363            }
364            default: gb_assert(0); break;
365        }
366    }
367    ~difflineMode() {
368        for (int i = 0; i<count; ++i) {
369            GBS_free_regexpr(reg[i]);
370            reg[i] = NULL;
371        }
372    }
373
374    const char *get_error() const { return error; }
375    int get_count() const { return count; }
376
377    void replaceAll(char*& str) const {
378        for (int i = 0; i<count; ++i) {
379            size_t      matchlen;
380            const char *matched = GBS_regmatch_compiled(str, reg[i], &matchlen);
381
382            if (matched) {
383                char       *prefix = GB_strpartdup(str, matched-1);
384                const char *suffix = matched+matchlen;
385
386                freeset(str, GBS_global_string_copy("%s%s%s", prefix, replace[i], suffix));
387                free(prefix);
388            }
389        }
390    }
391    void replaceAll(char*& str1, char*& str2) const { replaceAll(str1); replaceAll(str2); }
392};
393
394bool difflineMode::is_may = strstr(GB_date_string(), "May") != 0;
395
396static void cutEOL(char *s) {
397    char *lf      = strchr(s, '\n');
398    if (lf) lf[0] = 0;
399}
400
401static bool test_accept_diff_lines(const char *line1, const char *line2, const difflineMode& mode) {
402    if (*line1++ != '-') return false;
403    if (*line2++ != '+') return false;
404
405    char *dup1 = strdup(line1);
406    char *dup2 = strdup(line2);
407
408    cutEOL(dup1); // side-effect: accepts missing trailing newline
409    cutEOL(dup2);
410
411    mode.replaceAll(dup1, dup2);
412
413    bool equalNow = strcmp(dup1, dup2) == 0;
414#if defined(DEBUG)
415    // printf("dup1='%s'\ndup2='%s'\n", dup1, dup2); // uncomment this line to trace replaces
416#endif // DEBUG
417
418    free(dup2);
419    free(dup1);
420
421    return equalNow;
422}
423
424class DiffLines {
425    typedef std::list<std::string> Lines;
426    typedef Lines::iterator        LinesIter;
427    typedef Lines::const_iterator  LinesCIter;
428
429    Lines added_lines;
430    Lines deleted_lines;
431
432    LinesIter added_last_checked;
433    LinesIter deleted_last_checked;
434
435    static LinesIter next(LinesIter iter) { advance(iter, 1); return iter; }
436    static LinesIter last(Lines& lines) { return (++lines.rbegin()).base(); }
437
438    void set_checked() {
439        added_last_checked   = last(added_lines);
440        deleted_last_checked = last(deleted_lines);
441    }
442
443public:
444    DiffLines() { set_checked(); }
445
446    bool add(const char *diffline) {
447        bool did_add = true;
448        switch (diffline[0]) {
449            case '-': deleted_lines.push_back(diffline); break;
450            case '+': added_lines.push_back(diffline); break;
451            default : did_add = false; break;
452        }
453        // fputs(diffline, stdout); // uncomment to show all difflines
454        return did_add;
455    }
456
457    int added() const  { return added_lines.size(); }
458    int deleted() const  { return deleted_lines.size(); }
459
460    void remove_accepted_lines(const difflineMode& mode) {
461        LinesIter d    = next(deleted_last_checked);
462        LinesIter dEnd = deleted_lines.end();
463        LinesIter a    = next(added_last_checked);
464        LinesIter aEnd = added_lines.end();
465
466
467        while (d != dEnd && a != aEnd) {
468            if (test_accept_diff_lines(d->c_str(), a->c_str(), mode)) {
469                d = deleted_lines.erase(d);
470                a = added_lines.erase(a);
471            }
472            else {
473                ++d;
474                ++a;
475            }
476        }
477        set_checked();
478    }
479
480    void print_from(FILE *out, LinesCIter a, LinesCIter d) const {
481        LinesCIter dEnd = deleted_lines.end();
482        LinesCIter aEnd = added_lines.end();
483
484        while (d != dEnd && a != aEnd) {
485            fputs(d->c_str(), out); ++d;
486            fputs(a->c_str(), out); ++a;
487        }
488        while (d != dEnd) { fputs(d->c_str(), out); ++d; }
489        while (a != aEnd) { fputs(a->c_str(), out); ++a; }
490    }
491   
492    void print(FILE *out) const {
493        LinesCIter d = deleted_lines.begin();
494        LinesCIter a = added_lines.begin();
495        print_from(out, a, d);
496    }
497};
498
499
500bool GB_test_textfile_difflines(const char *file1, const char *file2, int expected_difflines, int special_mode) {
501    // special_mode: 0 = none, 1 = accept date and time changes as equal
502    const char *error   = NULL;
503
504    if      (!GB_is_regularfile(file1)) error = GBS_global_string("No such file '%s'", file1);
505    else if (!GB_is_regularfile(file2)) error = GBS_global_string("No such file '%s'", file2);
506    else {
507        char *cmd     = GBS_global_string_copy("/usr/bin/diff --unified %s %s", file1, file2);
508        FILE *diffout = popen(cmd, "r");
509
510        if (diffout) {
511#define BUFSIZE 5000
512            char         *buffer = (char*)malloc(BUFSIZE);
513            bool          inHunk = false;
514            DiffLines     diff_lines;
515            difflineMode  mode(special_mode);
516
517            TEST_ASSERT_NO_ERROR(mode.get_error());
518
519            while (!feof(diffout)) {
520                char *line = fgets(buffer, BUFSIZE, diffout);
521                if (!line) break;
522
523#if defined(ASSERTION_USED)
524                size_t len = strlen(line);
525                gb_assert(line && len<(BUFSIZE-1)); // increase BUFSIZE
526#endif
527
528                bool remove_now = true;
529                if (strncmp(line, "@@", 2) == 0) inHunk = true;
530                else if (!inHunk && strncmp(line, "Index: ", 7) == 0) inHunk = false;
531                else if (inHunk) {
532                    remove_now = !diff_lines.add(line);
533                }
534
535                if (remove_now) diff_lines.remove_accepted_lines(mode);
536            }
537
538            diff_lines.remove_accepted_lines(mode);
539
540            int added   = diff_lines.added();
541            int deleted = diff_lines.deleted();
542
543            if (added != deleted) {
544                error = GBS_global_string("added lines (=%i) differ from deleted lines(=%i)", added, deleted);
545            }
546            else if (added != expected_difflines) {
547                error = GBS_global_string("files differ in %i lines (expected=%i)", added, expected_difflines);
548            }
549            if (error) {
550                fputs("Different lines:\n", stdout);
551                diff_lines.print(stdout);
552                fputc('\n', stdout);
553            }
554
555            free(buffer);
556            IF_ASSERTION_USED(int err =) pclose(diffout);
557            gb_assert(err != -1);
558#undef BUFSIZE
559        }
560        else {
561            error = GBS_global_string("failed to run diff (%s)", cmd);
562        }
563        free(cmd);
564    }
565    // return result;
566    if (error) printf("GB_test_textfile_difflines(%s, %s) fails: %s\n", file1, file2, error);
567    return !error;
568}
569
570size_t GB_test_mem_equal(const unsigned char *buf1, const unsigned char *buf2, size_t common) { // used by unit tests
571    size_t equal_bytes;
572    if (memcmp(buf1, buf2, common) == 0) {
573        equal_bytes = common;
574    }
575    else {
576        equal_bytes = 0;
577        size_t x    = 0;    // position inside memory
578        while (buf1[x] == buf2[x]) {
579            x++;
580            equal_bytes++;
581        }
582
583        const size_t DUMP       = 7;
584        size_t       y1         = x >= DUMP ? x-DUMP : 0;
585        size_t       y2         = (x+DUMP)>common ? common : (x+DUMP);
586        size_t       blockstart = equal_bytes-x;
587
588        for (size_t y = y1; y <= y2; y++) {
589            fprintf(stderr, "[0x%04zx]", blockstart+y);
590            arb_test::print_pair(buf1[y], buf2[y]);
591            fputc(' ', stderr);
592            arb_test::print_hex_pair(buf1[y], buf2[y]);
593            if (x == y) fputs("                     <- diff", stderr);
594            fputc('\n', stderr);
595        }
596        if (y2 == common) {
597            fputs("[end of block - truncated]\n", stderr);
598        }
599    }
600    return equal_bytes;
601}
602
603bool GB_test_files_equal(const char *file1, const char *file2) {
604    const char        *error = NULL;
605    FILE              *fp1   = fopen(file1, "rb");
606
607    if (!fp1) {
608        printf("can't open '%s'\n", file1);
609        error = "i/o error";
610    }
611    else {
612        FILE *fp2 = fopen(file2, "rb");
613        if (!fp2) {
614            printf("can't open '%s'\n", file2);
615            error = "i/o error";
616        }
617        else {
618            const int      BLOCKSIZE    = 4096;
619            unsigned char *buf1         = (unsigned char*)malloc(BLOCKSIZE);
620            unsigned char *buf2         = (unsigned char*)malloc(BLOCKSIZE);
621            int            equal_bytes  = 0;
622
623            while (!error) {
624                int    read1  = fread(buf1, 1, BLOCKSIZE, fp1);
625                int    read2  = fread(buf2, 1, BLOCKSIZE, fp2);
626                size_t common = read1<read2 ? read1 : read2;
627
628                if (!common) {
629                    if (read1 != read2) error = "filesize differs";
630                    break;
631                }
632
633                size_t thiseq = GB_test_mem_equal(buf1, buf2, common);
634                if (thiseq != common) {
635                    error = "content differs";
636                }
637                equal_bytes += thiseq;
638            }
639
640            if (error) printf("test_files_equal: equal_bytes=%i\n", equal_bytes);
641            gb_assert(error || equal_bytes); // comparing empty files is nonsense
642
643            free(buf2);
644            free(buf1);
645            fclose(fp2);
646        }
647        fclose(fp1);
648    }
649
650    if (error) printf("test_files_equal(%s, %s) fails: %s\n", file1, file2, error);
651    return !error;
652}
653
654void TEST_diff_files() {
655    const char *file              = "diff/base.input";
656    const char *file_swapped      = "diff/swapped.input";
657    const char *file_date_swapped = "diff/date_swapped.input";
658    const char *file_date_changed = "diff/date_changed.input";
659
660    TEST_ASSERT(GB_test_textfile_difflines(file, file, 0, 0)); // check identity
661
662    // check if swapped lines are detected properly
663    TEST_ASSERT(GB_test_textfile_difflines(file, file_swapped, 1, 0));
664    TEST_ASSERT(GB_test_textfile_difflines(file, file_swapped, 1, 1));
665    TEST_ASSERT(GB_test_textfile_difflines(file, file_date_swapped, 3, 0));
666    TEST_ASSERT(GB_test_textfile_difflines(file, file_date_swapped, 3, 1));
667
668    TEST_ASSERT(GB_test_textfile_difflines(file, file_date_changed, 0, 1));
669    TEST_ASSERT(GB_test_textfile_difflines(file, file_date_changed, 6, 0));
670   
671    TEST_ASSERT(GB_test_textfile_difflines(file_date_swapped, file_date_changed, 6, 0));
672    TEST_ASSERT(GB_test_textfile_difflines(file_date_swapped, file_date_changed, 0, 1));
673}
674
675static char *remove_path(const char *fullname, void *cl_path) {
676    const char *path   = (const char *)cl_path;
677    return strdup(fullname+(ARB_strscmp(fullname, path) == 0 ? strlen(path) : 0));
678}
679
680static void GBT_transform_names(StrArray& dest, const StrArray& source, char *transform(const char *, void *), void *client_data) {
681    for (int i = 0; source[i]; ++i) dest.put(transform(source[i], client_data));
682}
683
684#define TEST_JOINED_DIR_CONTENT_EQUALS(subdir,mask,expected) do {       \
685        char     *fulldir = strdup(GB_path_in_ARBHOME(subdir));         \
686        StrArray  contents;                                             \
687        GBS_read_dir(contents, fulldir, mask);                          \
688        StrArray  contents_no_path;                                     \
689        GBT_transform_names(contents_no_path, contents, remove_path, (void*)fulldir); \
690        char     *joined  = GBT_join_names(contents_no_path, '!');      \
691        TEST_ASSERT_EQUAL(joined, expected);                            \
692        free(joined);                                                   \
693        free(fulldir);                                                  \
694    } while(0)
695
696void TEST_GBS_read_dir() {
697    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");
698    TEST_JOINED_DIR_CONTENT_EQUALS("GDE/CLUSTAL", "/s.*\\.c/", "/clustalv.c!/myers.c!/sequence.c!/showpair.c!/trees.c");
699    TEST_JOINED_DIR_CONTENT_EQUALS("GDE",         NULL,        "/Makefile!/README");
700    TEST_JOINED_DIR_CONTENT_EQUALS("GDE",         "*",         "/Makefile!/README");
701}
702
703void TEST_find_file() {
704    TEST_ASSERT_EQUAL(GB_existing_file("min_ascii.arb", false), "min_ascii.arb");
705    TEST_ASSERT_NULL(GB_existing_file("nosuchfile", false));
706   
707    char *tcporg = GB_lib_file(false, "", "arb_tcp_org.dat");
708    TEST_ASSERT_EQUAL(tcporg, GB_path_in_ARBHOME("lib/arb_tcp_org.dat"));
709    TEST_ASSERT_NULL(GB_lib_file(true, "bla", "blub"));
710    free(tcporg);
711
712    char *status = GB_property_file(false, "status.arb");
713    TEST_ASSERT_EQUAL(status, GB_path_in_ARBHOME("lib/arb_default/status.arb"));
714    TEST_ASSERT_NULL(GB_property_file(true, "undhepp"));
715    free(status);
716}
717
718// --------------------------------------------------------------------------------
719// tests for global code included from central ARB headers (located in $ARBHOME/TEMPLATES)
720#if defined(WARN_TODO)
721#warning move to ARB_CORE library
722#endif
723
724// gcc reports: "warning: logical 'or' of collectively exhaustive tests is always true"
725// for 'implicated(any, any)'. True, obviously. Nevertheless annoying.
726#pragma GCC diagnostic ignored "-Wlogical-op"
727
728void TEST_logic() {
729#define FOR_ANY_BOOL(name) for (int name = 0; name<2; ++name)
730
731    TEST_ASSERT(implicated(true, true));
732    // for (int any = 0; any<2; ++any) {
733    FOR_ANY_BOOL(any) {
734        TEST_ASSERT(implicated(false, any)); // "..aus Falschem folgt Beliebiges.."
735        TEST_ASSERT(implicated(any, any));
736
737        TEST_ASSERT( correlated(any, any));
738        TEST_ASSERT(!correlated(any, !any));
739        TEST_ASSERT(!contradicted(any, any));
740        TEST_ASSERT( contradicted(any, !any));
741    }
742
743    TEST_ASSERT(correlated(false, false));
744
745    TEST_ASSERT(contradicted(true, false));
746    TEST_ASSERT(contradicted(false, true));
747
748    FOR_ANY_BOOL(kermitIsFrog) {
749        FOR_ANY_BOOL(kermitIsGreen) {
750            bool allFrogsAreGreen  = implicated(kermitIsFrog, kermitIsGreen);
751            bool onlyFrogsAreGreen = implicated(kermitIsGreen, kermitIsFrog);
752
753            TEST_ASSERT(implicated( kermitIsFrog  && allFrogsAreGreen,  kermitIsGreen));
754            TEST_ASSERT(implicated(!kermitIsGreen && allFrogsAreGreen, !kermitIsFrog));
755
756            TEST_ASSERT(implicated( kermitIsGreen && onlyFrogsAreGreen,  kermitIsFrog));
757            TEST_ASSERT(implicated(!kermitIsFrog  && onlyFrogsAreGreen, !kermitIsGreen));
758
759            TEST_ASSERT(implicated(kermitIsFrog  && !kermitIsGreen, !allFrogsAreGreen));
760            TEST_ASSERT(implicated(kermitIsGreen && !kermitIsFrog,  !onlyFrogsAreGreen));
761
762            TEST_ASSERT(correlated(
763                            correlated(kermitIsGreen, kermitIsFrog), 
764                            allFrogsAreGreen && onlyFrogsAreGreen
765                            ));
766        }
767    }
768}
769
770
771#endif // UNIT_TESTS
Note: See TracBrowser for help on using the browser.