source: tags/svn.1.5.4/ARBDB/adfile.cxx

Last change on this file was 8319, checked in by westram, 14 years ago
  • ignored PERL2ARB interface as referrer (to detect functions that are only used from perl)
    • moved several functions to static scope or removed them (partly reverted by [13155])
    • for some functions it's ok to be only used from perl (e.g. macro support functions). Added comments there!
  • there is still some dead code in there, e.g.
    • read-security is implemented, but unused (and unwanted)
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 26.1 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 <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 is a NULL terminated array of char* (sorted alphanumerically)
230     * Use GBT_free_names() to free the result.
231     *
232     * In case of error, result is NULL 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)) {
244            names.put(strdup(fulldir));
245        }
246        else {
247            char *lslash = strrchr(fulldir, '/');
248
249            if (lslash) {
250                lslash[0]  = 0;
251                char *name = lslash+1;
252                if (GB_is_directory(fulldir)) {
253                    GBS_read_dir(names, fulldir, name);
254                }
255                lslash[0] = '/';
256            }
257
258            if (names.empty()) GB_export_errorf("can't read directory '%s'", fulldir);
259        }
260    }
261    else {
262        if (mask == NULL) mask = "*";
263
264        GBS_string_matcher *matcher = GBS_compile_matcher(mask, GB_MIND_CASE);
265        if (matcher) {
266            dirent *entry;
267            while ((entry = readdir(dirstream)) != 0) {
268                const char *name = entry->d_name;
269
270                if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
271                    ; // skip '.' and '..'
272                }
273                else {
274                    if (GBS_string_matches_regexp(name, matcher)) {
275                        const char *full = GB_concat_path(fulldir, name);
276                        if (!GB_is_directory(full)) { // skip directories
277                            names.put(strdup(full));
278                        }
279                    }
280                }
281            }
282
283            names.sort(GB_string_comparator, 0);
284
285            GBS_free_matcher(matcher);
286        }
287
288        closedir(dirstream);
289    }
290
291    free(fulldir);
292}
293
294// --------------------------------------------------------------------------------
295
296#ifdef UNIT_TESTS
297#include <test_unit.h>
298
299// GB_test_textfile_difflines + GB_test_files_equal are helper functions used
300// by unit tests.
301//
302// @@@ GB_test_textfile_difflines + GB_test_files_equal -> ARBCORE
303
304#define MAX_REGS 13
305
306class difflineMode : virtual Noncopyable {
307    int               mode;
308    GBS_regex        *reg[MAX_REGS];
309    const char       *replace[MAX_REGS];
310    int               count;
311    mutable GB_ERROR  error;
312
313    static bool is_may;
314
315    void add(const char *regEx, GB_CASE case_flag, const char *replacement) {
316        if (!error) {
317            gb_assert(count<MAX_REGS);
318            reg[count] = GBS_compile_regexpr(regEx, case_flag, &error);
319            if (!error) {
320                replace[count] = replacement;
321                count++;
322            }
323        }
324    }
325
326public:
327    difflineMode(int mode_)
328        : mode(mode_),
329          count(0),
330          error(NULL)
331    {
332        memset(reg, 0, sizeof(reg));
333        switch (mode) {
334            case 0: break;
335            case 1:  {
336                add("[0-9]{2}:[0-9]{2}:[0-9]{2}", GB_MIND_CASE, "<TIME>");
337                add("(Mon|Tue|Wed|Thu|Fri|Sat|Sun)", GB_IGNORE_CASE, "<DOW>");
338
339                add("(January|February|March|April|May|June|July|August|September|October|November|December)", GB_IGNORE_CASE, "<Month>");
340                add("(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)", GB_IGNORE_CASE, "<MON>");
341
342                add("<MON>[ -][0-9]{4}",   GB_IGNORE_CASE, "<MON> <YEAR>");
343                add("<Month>[ -][0-9]{4}", GB_IGNORE_CASE, "<Month> <YEAR>");
344
345                add("<TIME>[ -][0-9]{4}",  GB_IGNORE_CASE, "<TIME> <YEAR>");
346
347                add("<MON>[ -][0-9 ]?[0-9]",   GB_IGNORE_CASE, "<MON> <DAY>");
348                add("<Month>[ -][0-9 ]?[0-9]", GB_IGNORE_CASE, "<Month> <DAY>");
349
350                add("[0-9]{2}[ -]<MON>",   GB_IGNORE_CASE, "<DAY> <MON>");
351                add("[0-9]{2}[ -]<Month>", GB_IGNORE_CASE, "<DAY> <Month>");
352
353                add("<DAY>, [0-9]{4}", GB_IGNORE_CASE, "<DAY> <YEAR>");
354
355                if (is_may) {
356                    // 'May' does not differ between short/long monthname
357                    // -> use less accurate tests in May
358                    add("<Month>", GB_MIND_CASE, "<MON>");
359                }
360
361                break;
362            }
363            default: gb_assert(0); break;
364        }
365    }
366    ~difflineMode() {
367        for (int i = 0; i<count; ++i) {
368            GBS_free_regexpr(reg[i]);
369            reg[i] = NULL;
370        }
371    }
372
373    const char *get_error() const { return error; }
374    int get_count() const { return count; }
375
376    void replaceAll(char*& str) const {
377        for (int i = 0; i<count; ++i) {
378            size_t      matchlen;
379            const char *matched = GBS_regmatch_compiled(str, reg[i], &matchlen);
380
381            if (matched) {
382                char       *prefix = GB_strpartdup(str, matched-1);
383                const char *suffix = matched+matchlen;
384
385                freeset(str, GBS_global_string_copy("%s%s%s", prefix, replace[i], suffix));
386                free(prefix);
387            }
388        }
389    }
390    void replaceAll(char*& str1, char*& str2) const { replaceAll(str1); replaceAll(str2); }
391};
392
393bool difflineMode::is_may = strstr(GB_date_string(), "May") != 0;
394
395static void cutEOL(char *s) {
396    char *lf      = strchr(s, '\n');
397    if (lf) lf[0] = 0;
398}
399
400static bool test_accept_diff_lines(const char *line1, const char *line2, const difflineMode& mode) {
401    if (*line1++ != '-') return false;
402    if (*line2++ != '+') return false;
403
404    char *dup1 = strdup(line1);
405    char *dup2 = strdup(line2);
406
407    cutEOL(dup1); // side-effect: accepts missing trailing newline
408    cutEOL(dup2);
409
410    mode.replaceAll(dup1, dup2);
411
412    bool equalNow = strcmp(dup1, dup2) == 0;
413#if defined(DEBUG)
414    // printf("dup1='%s'\ndup2='%s'\n", dup1, dup2); // uncomment this line to trace replaces
415#endif // DEBUG
416
417    free(dup2);
418    free(dup1);
419
420    return equalNow;
421}
422
423class DiffLines {
424    typedef std::list<std::string> Lines;
425    typedef Lines::iterator        LinesIter;
426    typedef Lines::const_iterator  LinesCIter;
427
428    Lines added_lines;
429    Lines deleted_lines;
430
431    LinesIter added_last_checked;
432    LinesIter deleted_last_checked;
433
434    static LinesIter next(LinesIter iter) { advance(iter, 1); return iter; }
435    static LinesIter last(Lines& lines) { return (++lines.rbegin()).base(); }
436
437    void set_checked() {
438        added_last_checked   = last(added_lines);
439        deleted_last_checked = last(deleted_lines);
440    }
441
442public:
443    DiffLines() { set_checked(); }
444
445    bool add(const char *diffline) {
446        bool did_add = true;
447        switch (diffline[0]) {
448            case '-': deleted_lines.push_back(diffline); break;
449            case '+': added_lines.push_back(diffline); break;
450            default : did_add = false; break;
451        }
452        // fputs(diffline, stdout); // uncomment to show all difflines
453        return did_add;
454    }
455
456    int added() const  { return added_lines.size(); }
457    int deleted() const  { return deleted_lines.size(); }
458
459    void remove_accepted_lines(const difflineMode& mode) {
460        LinesIter d    = next(deleted_last_checked);
461        LinesIter dEnd = deleted_lines.end();
462        LinesIter a    = next(added_last_checked);
463        LinesIter aEnd = added_lines.end();
464
465
466        while (d != dEnd && a != aEnd) {
467            if (test_accept_diff_lines(d->c_str(), a->c_str(), mode)) {
468                d = deleted_lines.erase(d);
469                a = added_lines.erase(a);
470            }
471            else {
472                ++d;
473                ++a;
474            }
475        }
476        set_checked();
477    }
478
479    void print_from(FILE *out, LinesCIter a, LinesCIter d) const {
480        LinesCIter dEnd = deleted_lines.end();
481        LinesCIter aEnd = added_lines.end();
482
483        while (d != dEnd && a != aEnd) {
484            fputs(d->c_str(), out); ++d;
485            fputs(a->c_str(), out); ++a;
486        }
487        while (d != dEnd) { fputs(d->c_str(), out); ++d; }
488        while (a != aEnd) { fputs(a->c_str(), out); ++a; }
489    }
490   
491    void print(FILE *out) const {
492        LinesCIter d = deleted_lines.begin();
493        LinesCIter a = added_lines.begin();
494        print_from(out, a, d);
495    }
496};
497
498
499bool GB_test_textfile_difflines(const char *file1, const char *file2, int expected_difflines, int special_mode) {
500    // special_mode: 0 = none, 1 = accept date and time changes as equal
501    const char *error   = NULL;
502
503    if      (!GB_is_regularfile(file1)) error = GBS_global_string("No such file '%s'", file1);
504    else if (!GB_is_regularfile(file2)) error = GBS_global_string("No such file '%s'", file2);
505    else {
506        char *cmd     = GBS_global_string_copy("/usr/bin/diff --unified %s %s", file1, file2);
507        FILE *diffout = popen(cmd, "r");
508
509        if (diffout) {
510#define BUFSIZE 5000
511            char         *buffer = (char*)malloc(BUFSIZE);
512            bool          inHunk = false;
513            DiffLines     diff_lines;
514            difflineMode  mode(special_mode);
515
516            TEST_ASSERT_NO_ERROR(mode.get_error());
517
518            while (!feof(diffout)) {
519                char *line = fgets(buffer, BUFSIZE, diffout);
520                if (!line) break;
521
522#if defined(ASSERTION_USED)
523                size_t len = strlen(line);
524                gb_assert(line && len<(BUFSIZE-1)); // increase BUFSIZE
525#endif
526
527                bool remove_now = true;
528                if (strncmp(line, "@@", 2) == 0) inHunk = true;
529                else if (!inHunk && strncmp(line, "Index: ", 7) == 0) inHunk = false;
530                else if (inHunk) {
531                    remove_now = !diff_lines.add(line);
532                }
533
534                if (remove_now) diff_lines.remove_accepted_lines(mode);
535            }
536
537            diff_lines.remove_accepted_lines(mode);
538
539            int added   = diff_lines.added();
540            int deleted = diff_lines.deleted();
541
542            if (added != deleted) {
543                error = GBS_global_string("added lines (=%i) differ from deleted lines(=%i)", added, deleted);
544            }
545            else if (added != expected_difflines) {
546                error = GBS_global_string("files differ in %i lines (expected=%i)", added, expected_difflines);
547            }
548            if (error) {
549                fputs("Different lines:\n", stdout);
550                diff_lines.print(stdout);
551                fputc('\n', stdout);
552            }
553
554            free(buffer);
555            IF_ASSERTION_USED(int err =) pclose(diffout);
556            gb_assert(err != -1);
557#undef BUFSIZE
558        }
559        else {
560            error = GBS_global_string("failed to run diff (%s)", cmd);
561        }
562        free(cmd);
563    }
564    // return result;
565    if (error) printf("GB_test_textfile_difflines(%s, %s) fails: %s\n", file1, file2, error);
566    return !error;
567}
568
569size_t GB_test_mem_equal(const unsigned char *buf1, const unsigned char *buf2, size_t common) { // used by unit tests
570    size_t equal_bytes;
571    if (memcmp(buf1, buf2, common) == 0) {
572        equal_bytes = common;
573    }
574    else {
575        equal_bytes = 0;
576        size_t x    = 0;    // position inside memory
577        while (buf1[x] == buf2[x]) {
578            x++;
579            equal_bytes++;
580        }
581
582        const size_t DUMP       = 7;
583        size_t       y1         = x >= DUMP ? x-DUMP : 0;
584        size_t       y2         = (x+DUMP)>common ? common : (x+DUMP);
585        size_t       blockstart = equal_bytes-x;
586
587        for (size_t y = y1; y <= y2; y++) {
588            fprintf(stderr, "[0x%04zx]", blockstart+y);
589            arb_test::print_pair(buf1[y], buf2[y]);
590            fputc(' ', stderr);
591            arb_test::print_hex_pair(buf1[y], buf2[y]);
592            if (x == y) fputs("                     <- diff", stderr);
593            fputc('\n', stderr);
594        }
595        if (y2 == common) {
596            fputs("[end of block - truncated]\n", stderr);
597        }
598    }
599    return equal_bytes;
600}
601
602bool GB_test_files_equal(const char *file1, const char *file2) {
603    const char        *error = NULL;
604    FILE              *fp1   = fopen(file1, "rb");
605
606    if (!fp1) {
607        printf("can't open '%s'\n", file1);
608        error = "i/o error";
609    }
610    else {
611        FILE *fp2 = fopen(file2, "rb");
612        if (!fp2) {
613            printf("can't open '%s'\n", file2);
614            error = "i/o error";
615        }
616        else {
617            const int      BLOCKSIZE    = 4096;
618            unsigned char *buf1         = (unsigned char*)malloc(BLOCKSIZE);
619            unsigned char *buf2         = (unsigned char*)malloc(BLOCKSIZE);
620            int            equal_bytes  = 0;
621
622            while (!error) {
623                int    read1  = fread(buf1, 1, BLOCKSIZE, fp1);
624                int    read2  = fread(buf2, 1, BLOCKSIZE, fp2);
625                size_t common = read1<read2 ? read1 : read2;
626
627                if (!common) {
628                    if (read1 != read2) error = "filesize differs";
629                    break;
630                }
631
632                size_t thiseq = GB_test_mem_equal(buf1, buf2, common);
633                if (thiseq != common) {
634                    error = "content differs";
635                }
636                equal_bytes += thiseq;
637            }
638
639            if (error) printf("test_files_equal: equal_bytes=%i\n", equal_bytes);
640            gb_assert(error || equal_bytes); // comparing empty files is nonsense
641
642            free(buf2);
643            free(buf1);
644            fclose(fp2);
645        }
646        fclose(fp1);
647    }
648
649    if (error) printf("test_files_equal(%s, %s) fails: %s\n", file1, file2, error);
650    return !error;
651}
652
653void TEST_diff_files() {
654    const char *file              = "diff/base.input";
655    const char *file_swapped      = "diff/swapped.input";
656    const char *file_date_swapped = "diff/date_swapped.input";
657    const char *file_date_changed = "diff/date_changed.input";
658
659    TEST_ASSERT(GB_test_textfile_difflines(file, file, 0, 0)); // check identity
660
661    // check if swapped lines are detected properly
662    TEST_ASSERT(GB_test_textfile_difflines(file, file_swapped, 1, 0));
663    TEST_ASSERT(GB_test_textfile_difflines(file, file_swapped, 1, 1));
664    TEST_ASSERT(GB_test_textfile_difflines(file, file_date_swapped, 3, 0));
665    TEST_ASSERT(GB_test_textfile_difflines(file, file_date_swapped, 3, 1));
666
667    TEST_ASSERT(GB_test_textfile_difflines(file, file_date_changed, 0, 1));
668    TEST_ASSERT(GB_test_textfile_difflines(file, file_date_changed, 6, 0));
669   
670    TEST_ASSERT(GB_test_textfile_difflines(file_date_swapped, file_date_changed, 6, 0));
671    TEST_ASSERT(GB_test_textfile_difflines(file_date_swapped, file_date_changed, 0, 1));
672}
673
674static char *remove_path(const char *fullname, void *cl_path) {
675    const char *path   = (const char *)cl_path;
676    return strdup(fullname+(ARB_strscmp(fullname, path) == 0 ? strlen(path) : 0));
677}
678
679static void GBT_transform_names(StrArray& dest, const StrArray& source, char *transform(const char *, void *), void *client_data) {
680    for (int i = 0; source[i]; ++i) dest.put(transform(source[i], client_data));
681}
682
683#define TEST_JOINED_DIR_CONTENT_EQUALS(subdir,mask,expected) do {       \
684        char     *fulldir = strdup(GB_path_in_ARBHOME(subdir));         \
685        StrArray  contents;                                             \
686        GBS_read_dir(contents, fulldir, mask);                          \
687        StrArray  contents_no_path;                                     \
688        GBT_transform_names(contents_no_path, contents, remove_path, (void*)fulldir); \
689        char     *joined  = GBT_join_names(contents_no_path, '!');      \
690        TEST_ASSERT_EQUAL(joined, expected);                            \
691        free(joined);                                                   \
692        free(fulldir);                                                  \
693    } while(0)
694
695void TEST_GBS_read_dir() {
696    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");
697    TEST_JOINED_DIR_CONTENT_EQUALS("GDE/CLUSTAL", "/s.*\\.c/", "/clustalv.c!/myers.c!/sequence.c!/showpair.c!/trees.c");
698    TEST_JOINED_DIR_CONTENT_EQUALS("GDE",         NULL,        "/Makefile!/README");
699}
700
701void TEST_find_file() {
702    TEST_ASSERT_EQUAL(GB_existing_file("min_ascii.arb", false), "min_ascii.arb");
703    TEST_ASSERT_NULL(GB_existing_file("nosuchfile", false));
704   
705    char *tcporg = GB_lib_file(false, "", "arb_tcp_org.dat");
706    TEST_ASSERT_EQUAL(tcporg, GB_path_in_ARBHOME("lib/arb_tcp_org.dat"));
707    TEST_ASSERT_NULL(GB_lib_file(true, "bla", "blub"));
708    free(tcporg);
709
710    char *status = GB_property_file(false, "status.arb");
711    TEST_ASSERT_EQUAL(status, GB_path_in_ARBHOME("lib/arb_default/status.arb"));
712    TEST_ASSERT_NULL(GB_property_file(true, "undhepp"));
713    free(status);
714}
715
716// --------------------------------------------------------------------------------
717// tests for global code included from central ARB headers (located in $ARBHOME/TEMPLATES)
718#if defined(WARN_TODO)
719#warning move to ARB_CORE library
720#endif
721
722// gcc reports: "warning: logical 'or' of collectively exhaustive tests is always true"
723// for 'implicated(any, any)'. True, obviously. Nevertheless annoying.
724#pragma GCC diagnostic ignored "-Wlogical-op"
725
726void TEST_logic() {
727#define FOR_ANY_BOOL(name) for (int name = 0; name<2; ++name)
728
729    TEST_ASSERT(implicated(true, true));
730    // for (int any = 0; any<2; ++any) {
731    FOR_ANY_BOOL(any) {
732        TEST_ASSERT(implicated(false, any)); // "..aus Falschem folgt Beliebiges.."
733        TEST_ASSERT(implicated(any, any));
734
735        TEST_ASSERT( correlated(any, any));
736        TEST_ASSERT(!correlated(any, !any));
737        TEST_ASSERT(!contradicted(any, any));
738        TEST_ASSERT( contradicted(any, !any));
739    }
740
741    TEST_ASSERT(correlated(false, false));
742
743    TEST_ASSERT(contradicted(true, false));
744    TEST_ASSERT(contradicted(false, true));
745
746    FOR_ANY_BOOL(kermitIsFrog) {
747        FOR_ANY_BOOL(kermitIsGreen) {
748            bool allFrogsAreGreen  = implicated(kermitIsFrog, kermitIsGreen);
749            bool onlyFrogsAreGreen = implicated(kermitIsGreen, kermitIsFrog);
750
751            TEST_ASSERT(implicated( kermitIsFrog  && allFrogsAreGreen,  kermitIsGreen));
752            TEST_ASSERT(implicated(!kermitIsGreen && allFrogsAreGreen, !kermitIsFrog));
753
754            TEST_ASSERT(implicated( kermitIsGreen && onlyFrogsAreGreen,  kermitIsFrog));
755            TEST_ASSERT(implicated(!kermitIsFrog  && onlyFrogsAreGreen, !kermitIsGreen));
756
757            TEST_ASSERT(implicated(kermitIsFrog  && !kermitIsGreen, !allFrogsAreGreen));
758            TEST_ASSERT(implicated(kermitIsGreen && !kermitIsFrog,  !onlyFrogsAreGreen));
759
760            TEST_ASSERT(correlated(
761                            correlated(kermitIsGreen, kermitIsFrog), 
762                            allFrogsAreGreen && onlyFrogsAreGreen
763                            ));
764        }
765    }
766}
767
768
769#endif // UNIT_TESTS
Note: See TracBrowser for help on using the repository browser.