source: branches/profile/CONVERTALN/main.cxx

Last change on this file was 11626, checked in by westram, 10 years ago
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.0 KB
Line 
1// ------------------------------------------------------------
2//
3// Format Conversion Program.
4//
5// Woese Lab., Dept. of Microbiology, UIUC
6// Modified for use in ARB by ARB team
7//
8// ------------------------------------------------------------
9
10#include "defs.h"
11#include "fun.h"
12#include "global.h"
13
14Convaln_exception *Convaln_exception::thrown = NULL;
15
16struct TypeSwitch { const char *switchtext; Format format; };
17
18static TypeSwitch convertible_type[] = { // see fconv.cxx@format_spec
19    { "GenBank",   GENBANK   },
20    { "EMBL",      EMBL      },
21    { "AE2",       MACKE     },
22    { "SwissProt", SWISSPROT },
23    { "NEXUS",     NEXUS     },
24    { "PHYLIP",    PHYLIP    },
25    { "FASTDNAML", FASTDNAML },
26    { "GCG",       GCG       },
27    { "PRINTABLE", PRINTABLE },
28};
29
30static void show_command_line_usage() {
31    fputs("Command line usage:\n"
32          "  $ arb_convert_aln [--arb-notify] -INFMT input_file -OUTFMT output_file\n"
33          "\n"
34          "  where\n"
35          "      INFMT  may be 'GenBank', 'EMBL', 'AE2' or 'SwissProt' and\n"
36          "      OUTFMT may be 'GenBank', 'EMBL', 'AE2', 'NEXUS', 'PHYLIP', 'FASTDNAML', 'GCG' or 'Printable'\n"
37          "  (Note: you may abbreviate the format names)\n"
38          "\n"
39          "  FASTDNAML writes a PHYLIP file with content from STDIN appended at end of first line (used for arb_fastdnaml).\n"
40          "\n"
41          "  if argument '--arb-notify' is given, arb_convert_aln assumes it has been started by ARB\n"
42          "  and reports errors using the 'arb_message' script.\n"
43          , stderr);
44}
45
46static void valid_name_or_die(const char *file_name) {
47    if (str0len(file_name) <= 0) {
48        throw_errorf(152, "illegal file name: %s", file_name);
49    }
50}
51static bool file_exists(const char *file_name) {
52    FILE *ifp    = fopen(file_name, "r");
53    bool  exists = ifp != NULL;
54    if (ifp) fclose(ifp);
55
56    return exists;
57}
58
59static void change_file_suffix(const char *old_file, char *file_name, int type) {
60    // Define the default file name by changing suffix.
61    int indi, indj;
62
63    for (indi = str0len(old_file) - 1; indi >= 0 && old_file[indi] != '.'; indi--)
64        if (indi == 0)
65            strcpy(file_name, old_file);
66        else {
67            for (indj = 0; indj < (indi - 1); indj++)
68                file_name[indj] = old_file[indj];
69            file_name[indj] = '\0';
70        }
71    switch (type) {
72        case GENBANK:
73            strcat(file_name, ".GB");
74            break;
75        case MACKE:
76            strcat(file_name, ".aln");
77            break;
78        case NEXUS:
79            strcat(file_name, ".NEXUS");
80            break;
81        case PHYLIP:
82            strcat(file_name, ".PHY");
83            break;
84        case EMBL:
85            strcat(file_name, ".EMBL");
86            break;
87        case PRINTABLE:
88            strcat(file_name, ".prt");
89            break;
90        default:
91            strcat(file_name, ".???");
92    }
93}
94
95static void ask_for_conversion_params(FormattedFile& in, FormattedFile& out) {
96    char temp[LINESIZE];
97    char choice[LINESIZE];
98
99    fputs("---------------------------------------------------------------\n"
100          "\n"
101          "  convert_aln - an alignment and file converter written by\n"
102          "  WenMin Kuan for the RDP database project.\n"
103          "\n"
104          "  Modified for use in ARB by Oliver Strunk & Ralf Westram\n"
105          "  Report errors or deficiencies to devel@arb-home.de\n"
106          "\n"
107          , stderr);
108    show_command_line_usage();
109    fputs("\n"
110          "---------------------------------------------------------------\n"
111          "\n"
112          "Select input format (<CR> means default)\n"
113          "\n"
114          "  (1)  GenBank [default]\n"
115          "  (2)  EMBL\n"
116          "  (3)  AE2\n"
117          "  (4)  SwissProt\n"
118          "  (5)  Quit\n"
119          "  ? "
120          , stderr);
121
122    Getstr(choice, LINESIZE);
123    {
124        Format inType = UNKNOWN;
125        switch (choice[0]) {
126            case '\0': // [default]
127            case '1': inType = GENBANK; break;
128            case '2': inType = EMBL; break;
129            case '3': inType = MACKE; break;
130            case '4': inType = SWISSPROT; break;
131            case '5': exit(0); // ok - interactive mode only
132            default: throw_errorf(16, "Unknown input format selection '%s'", choice);
133        }
134
135        fputs("\nInput file name? ", stderr);
136        Getstr(temp, LINESIZE);
137        in.init(temp, inType);
138    }
139
140    valid_name_or_die(temp);
141    if (!file_exists(temp)) throw_error(77, "Input file not found");
142
143    // output file information
144    fputs("\n"
145          "Select output format (<CR> means default)\n"
146          "\n"
147          "  (1)  GenBank\n"
148          "  (2)  EMBL\n"
149          "  (3)  AE2 [default]\n"
150          "  (4)  NEXUS (Paup)\n"
151          "  (5)  PHYLIP\n"
152          "  (6)  GCG\n"
153          "  (7)  Printable\n"
154          "  (8)  Quit\n"
155          "  ? ", stderr);
156
157    Getstr(choice, LINESIZE);
158    {
159        Format ouType = UNKNOWN;
160        switch (choice[0]) {
161            case '1': ouType = GENBANK; break;
162            case '2': ouType = EMBL; break;
163            case '\0': // [default]
164            case '3': ouType = MACKE; break;
165            case '4': ouType = NEXUS; break;
166            case '5': ouType = PHYLIP; break;
167            case '6': ouType = GCG; break;
168            case '7': ouType = PRINTABLE; break;
169            case '8': exit(0); // ok - interactive mode only
170            default: throw_errorf(66, "Unknown output format selection '%s'", choice);
171        }
172        change_file_suffix(in.name(), temp, ouType);
173        if (ouType != GCG) {
174            fprintf(stderr, "\nOutput file name [%s]? ", temp);
175            Getstr(temp, LINESIZE);
176            if (str0len(temp) == 0)
177                change_file_suffix(in.name(), temp, ouType);
178        }
179        out.init(temp, ouType);
180    }
181}
182
183static int strcasecmp_start(const char *s1, const char *s2) {
184    int cmp = 0;
185    for (int p = 0; !cmp; p++) {
186        cmp = tolower(s1[p])-tolower(s2[p]);
187        if (!s1[p]) return 0;
188    }
189    return cmp;
190}
191
192static bool is_abbrev_switch(const char *arg, const char *switchtext) {
193    return arg[0] == '-' && strcasecmp_start(arg+1, switchtext) == 0;
194}
195
196static Format parse_type(const char *arg) {
197    for (size_t i = 0; i<ARRAY_ELEMS(convertible_type); ++i) {
198        const TypeSwitch& type = convertible_type[i];
199        if (is_abbrev_switch(arg, type.switchtext)) {
200            return type.format;
201        }
202    }
203    return UNKNOWN;
204}
205
206static Format parse_intype(const char *arg) {
207    Format type = parse_type(arg);
208    if (!is_input_format(type)) throw_errorf(65, "Unsupported input file type '%s'", arg);
209    if (type == UNKNOWN) throw_errorf(67, "UNKNOWN input file type '%s'", arg);
210    return type;
211}
212
213static Format parse_outtype(const char *arg) {
214    Format type = parse_type(arg);
215    if (type == UNKNOWN) throw_errorf(68, "UNKNOWN output file type '%s'", arg);
216    return type;
217}
218
219static bool is_help_req(const char *arg) {
220    return strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0;
221}
222static bool command_line_conversion(int argc, const char * const *argv, FormattedFile& in, FormattedFile& out) {
223    for (int c = 1; c<argc; c++) {
224        if (is_help_req(argv[c])) {
225            show_command_line_usage();
226            return false;
227        }
228    }
229
230    if (argc != 5) throw_errorf(69, "arb_convert_aln expects exactly 4 parameters (you specified %i). Try '--help'", argc-1);
231
232    in.init(argv[2], parse_intype(argv[1]));
233    out.init(argv[4], parse_outtype(argv[3]));
234
235    return true;
236}
237
238static void do_conversion(const FormattedFile& in, const FormattedFile& out) {
239#ifdef CALOG
240    fprintf(stderr, "\n\nConvert file %s to file %s.\n", in.name(), out.name());
241#endif
242
243    // check if output file exists and filename's validation
244    valid_name_or_die(out.name());
245    if (file_exists(out.name())) warningf(151, "Output file %s exists, will be overwritten.", out.name());
246
247    // file format transfer...
248    convert(in, out);
249}
250
251int ARB_main(int argc, char *argv[]) {
252    int  exitcode        = EXIT_SUCCESS;
253    bool use_arb_message = false;
254    try {
255        FormattedFile in;
256        FormattedFile out;
257
258        if (argc>1 && strcmp(argv[1], "--arb-notify") == 0) {
259            use_arb_message = true;
260            argc--; argv++;
261        }
262
263        if (argc < 2) {
264            ask_for_conversion_params(in, out);
265            do_conversion(in, out);
266        }
267        else {
268            if (command_line_conversion(argc, argv, in, out)) {
269                do_conversion(in, out);
270            }
271        }
272    }
273    catch (Convaln_exception& err) {
274        fprintf(stderr, "ERROR(%d): %s\n", err.get_code(), err.get_msg());
275        if (use_arb_message) {
276            char *escaped = strdup(err.get_msg());
277            for (int i = 0; escaped[i]; ++i) if (escaped[i] == '\"') escaped[i] = '\'';
278
279            char *command        = strf("arb_message \"Error: %s (in arb_convert_aln; code=%d)\"", escaped, err.get_code());
280            if (system(command) != 0) fprintf(stderr, "ERROR running '%s'\n", command);
281            free(command);
282
283            free(escaped);
284        }
285        exitcode = EXIT_FAILURE;
286    }
287    return exitcode;
288}
289
290// --------------------------------------------------------------------------------
291
292#ifdef UNIT_TESTS
293#include <test_unit.h>
294
295void TEST_BASIC_switch_parsing() {
296    TEST_EXPECT_ZERO(strcasecmp_start("GenBank", "GenBank"));
297    TEST_EXPECT_ZERO(strcasecmp_start("GEnbaNK", "genBANK"));
298    TEST_EXPECT_ZERO(strcasecmp_start("Ge", "GenBank"));
299    TEST_EXPECT(strcasecmp_start("GenBank", "NEXUS") < 0);
300    TEST_EXPECT(strcasecmp_start("NEXUS", "GenBank") > 0);
301
302    TEST_REJECT(is_abbrev_switch("notAswitch", "notAswitch"));
303    TEST_REJECT(is_abbrev_switch("-GenbankPlus", "Genbank"));
304    TEST_REJECT(is_abbrev_switch("-Ge", "NEXUS"));
305
306    TEST_EXPECT(is_abbrev_switch("-Ge", "Genbank"));
307    TEST_EXPECT(is_abbrev_switch("-N", "NEXUS"));
308    TEST_EXPECT(is_abbrev_switch("-NEXUS", "NEXUS"));
309
310    TEST_EXPECT_EQUAL(parse_outtype("-PH"), PHYLIP);
311    TEST_EXPECT_EQUAL(parse_outtype("-PHYLIP"), PHYLIP);
312    TEST_EXPECT_EQUAL(parse_outtype("-phylip"), PHYLIP);
313}
314
315#endif // UNIT_TESTS
Note: See TracBrowser for help on using the repository browser.