~ubuntu-branches/debian/jessie/arb/jessie

« back to all changes in this revision

Viewing changes to PERLTOOLS/arb_proto_2_xsub.cxx

  • Committer: Package Import Robot
  • Author(s): Elmar Pruesse, Andreas Tille, Elmar Pruesse
  • Date: 2014-09-02 15:15:06 UTC
  • mfrom: (1.1.6)
  • Revision ID: package-import@ubuntu.com-20140902151506-jihq58b3iz342wif
Tags: 6.0.2-1
[ Andreas Tille ]
* New upstream version
  Closes: #741890
* debian/upstream -> debian/upstream/metadata
* debian/control:
   - Build-Depends: added libglib2.0-dev
   - Depends: added mafft, mrbayes
* debian/rules
   - Add explicite --remove-section=.comment option to manual strip call
* cme fix dpkg-control
* arb-common.dirs: Do not create unneeded lintian dir
* Add turkish debconf translation (thanks for the patch to Mert Dirik
  <mertdirik@gmail.com>)
  Closes: #757497

[ Elmar Pruesse ]
* patches removed:
   - 10_config.makefiles.patch,
     80_no_GL.patch
       removed in favor of creating file from config.makefile.template via 
       sed in debian/control
   - 20_Makefile_main.patch
       merged upstream
   - 21_Makefiles.patch
       no longer needed
   - 30_tmpfile_CVE-2008-5378.patch: 
       merged upstream
   - 50_fix_gcc-4.8.patch:
       merged upstream
   - 40_add_libGLU.patch:
       libGLU not needed for arb_ntree)
   - 60_use_debian_packaged_raxml.patch:
       merged upstream
   - 70_hardening.patch
       merged upstream
   - 72_add_math_lib_to_linker.patch
       does not appear to be needed
* patches added:
   - 10_upstream_r12793__show_db_load_progress:
       backported patch showing progress while ARB is loading a database
       (needed as indicator/splash screen while ARB is launching)
   - 20_upstream_r12794__socket_permissions:
       backported security fix
   - 30_upstream_r12814__desktop_keywords:
       backported add keywords to desktop (fixes lintian warning)
   - 40_upstream_r12815__lintian_spelling:
       backported fix for lintian reported spelling errors
   - 50_private_nameservers
       change configuration to put nameservers into users home dirs
       (avoids need for shared writeable directory)
   - 60_use_debian_phyml
       use phyml from debian package for both interfaces in ARB
* debian/rules:
   - create config.makefile from override_dh_configure target
   - use "make tarfile" in override_dh_install
   - remove extra cleaning not needed for ARB 6
   - use "dh_install --list-missing" to avoid missing files
   - added override_dh_fixperms target
* debian/control:
   - added libarb-dev package
   - Depends: added phyml, xdg-utils
   - Suggests: removed phyml
   - fix lintian duplicate-short-description (new descriptions)
* debian/*.install:
   - "unrolled" confusing globbing to select files
   - pick files from debian/tmp
   - moved all config files to /etc/arb
* debian/arb-common.templates: updated
* scripts:
   - removed arb-add-pt-server
   - launch-wrapper: 
     - only add demo.arb to newly created $ARBUSERDATA
     - pass commandline arguments through bin/arb wrapper
   - preinst: removing old PT server index files on upgrade from 5.5*
   - postinst: set setgid on shared PT dir
* rewrote arb.1 manfile
* added file icon for ARB databases
* using upstream arb_tcp.dat

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// ================================================================ //
 
2
//                                                                  //
 
3
//   File      : arb_proto_2_xsub.cxx                               //
 
4
//   Purpose   : generate ARB.xs for perl interface                 //
 
5
//                                                                  //
 
6
//   ReCoded by Ralf Westram (coder@reallysoft.de) in December 2009 //
 
7
//   Institute of Microbiology (Technical University Munich)        //
 
8
//   http://www.arb-home.de/                                        //
 
9
//                                                                  //
 
10
// ================================================================ //
 
11
 
 
12
#include <arbdb.h>
 
13
#include <arb_strbuf.h>
 
14
#include <BufferedFileReader.h>
 
15
 
 
16
#include <string>
 
17
#include <vector>
 
18
#include <set>
 
19
 
 
20
#include <cctype>
 
21
#include <unistd.h>
 
22
#include <arb_str.h>
 
23
#include <arb_diff.h>
 
24
 
 
25
#if defined(DEBUG)
 
26
#if defined(DEVEL_RALF)
 
27
// #define TRACE
 
28
#endif // DEVEL_RALF
 
29
#endif // DEBUG
 
30
 
 
31
using namespace std;
 
32
 
 
33
// --------------------------------------------------------------------------------
 
34
 
 
35
#define CHAR_PTR       "char *"
 
36
#define CONST_CHAR_PTR "const char *"
 
37
 
 
38
// --------------------------------------------------------------------------------
 
39
 
 
40
struct Error {
 
41
    virtual ~Error() {}
 
42
    virtual void print() const = 0;
 
43
};
 
44
 
 
45
class ProgramError : public Error {
 
46
    string error;
 
47
public:
 
48
    ProgramError(string message) : error(message) {}
 
49
    ProgramError(const char *message) : error(message) {}
 
50
    virtual ~ProgramError() OVERRIDE {}
 
51
 
 
52
    void print() const OVERRIDE {
 
53
        fprintf(stderr, "arb_proto_2_xsub: Error: %s\n", error.c_str());
 
54
    }
 
55
};
 
56
 
 
57
class InputFileError : public Error {
 
58
    string located_error;
 
59
public:
 
60
    InputFileError(LineReader& fileBuffer, string message)      : located_error(fileBuffer.lineError(message)) {}
 
61
    InputFileError(LineReader& fileBuffer, const char *message) : located_error(fileBuffer.lineError(message)) {}
 
62
    virtual ~InputFileError() OVERRIDE {}
 
63
 
 
64
    void print() const OVERRIDE {
 
65
        fputs(located_error.c_str(), stderr);
 
66
        fputc('\n', stderr);
 
67
    }
 
68
};
 
69
 
 
70
// --------------------------------------------------------------------------------
 
71
 
 
72
class CommentSkippingFileBuffer : public BufferedFileReader {
 
73
    string open_comment;
 
74
    string close_comment;
 
75
    string eol_comment;
 
76
 
 
77
    void throw_error(const char *message) __ATTR__NORETURN { throw InputFileError(*this, message); }
 
78
 
 
79
    string read_till_close_comment(string curr_line, size_t comment_startLineNumber) {
 
80
        bool seen_end = false;
 
81
        while (!seen_end) {
 
82
            size_t close = curr_line.find(close_comment);
 
83
            if (close != string::npos) {
 
84
                curr_line = curr_line.substr(close+close_comment.length());
 
85
                seen_end  = true;
 
86
            }
 
87
            else {
 
88
                if (!BufferedFileReader::getLine(curr_line)) {
 
89
                    setLineNumber(comment_startLineNumber);
 
90
                    throw_error("end of file reached while skipping comment");
 
91
                }
 
92
            }
 
93
        }
 
94
        return curr_line;
 
95
    }
 
96
 
 
97
public:
 
98
    CommentSkippingFileBuffer(const string&  filename_,
 
99
                              FILE          *in,
 
100
                              const char    *openComment,
 
101
                              const char    *closeComment,
 
102
                              const char    *eolComment)
 
103
        : BufferedFileReader(filename_, in)
 
104
        , open_comment(openComment)
 
105
        , close_comment(closeComment)
 
106
        , eol_comment(eolComment)
 
107
    {}
 
108
 
 
109
    bool getLine(string& line) OVERRIDE {
 
110
        if (BufferedFileReader::getLine(line)) {
 
111
            size_t open = line.find(open_comment);
 
112
            size_t eol  = line.find(eol_comment);
 
113
 
 
114
            if (open != eol) {                      // comment found
 
115
                if (open<eol) {
 
116
                    if (eol != string::npos) {
 
117
                        throw_error(GBS_global_string("'%s' inside '%s %s'", eol_comment.c_str(), open_comment.c_str(), close_comment.c_str()));
 
118
                    }
 
119
                    line = line.substr(0, open) + read_till_close_comment(line.substr(open+2), getLineNumber());
 
120
                }
 
121
                else {
 
122
                    arb_assert(eol<open);
 
123
                    if (open != string::npos) {
 
124
                        throw_error(GBS_global_string("'%s' behind '%s'", open_comment.c_str(), eol_comment.c_str()));
 
125
                    }
 
126
                    line = line.substr(0, eol);
 
127
                }
 
128
            }
 
129
            return true;
 
130
        }
 
131
        return false;
 
132
    }
 
133
};
 
134
 
 
135
// --------------------------------------------------------------------------------
 
136
 
 
137
inline bool is_empty_code(const char *code) { return !code[0]; }
 
138
inline bool contains_preprozessorCode(const char *code) { return strchr(code, '#') != NULL; }
 
139
inline bool contains_braces(const char *code) { return strpbrk(code, "{}") != NULL; }
 
140
inline bool is_typedef(const char *code) { return ARB_strBeginsWith(code, "typedef"); }
 
141
inline bool is_forward_decl(const char *code) { return ARB_strBeginsWith(code, "class") || ARB_strBeginsWith(code, "struct"); }
 
142
 
 
143
inline bool is_prototype(const char *code) {
 
144
    return
 
145
        !is_empty_code(code)             &&
 
146
        !contains_preprozessorCode(code) &&
 
147
        !contains_braces(code)           &&
 
148
        !is_typedef(code)                &&
 
149
        !is_forward_decl(code);
 
150
}
 
151
 
 
152
inline void trace_over_braces(const char *code, int& brace_counter) {
 
153
    while (code) {
 
154
        const char *brace = strpbrk(code, "{}");
 
155
        if (!brace) break;
 
156
 
 
157
        if (*brace == '{') {
 
158
            ++brace_counter;
 
159
        }
 
160
        else {
 
161
            arb_assert(*brace == '}');
 
162
            --brace_counter;
 
163
        }
 
164
        code = brace+1;
 
165
    }
 
166
}
 
167
 
 
168
// --------------------------------------------------------------------------------
 
169
 
 
170
inline char *get_token_and_incr_lineno(const char*& code, const char *separator, size_t *lineno) {
 
171
    char *token = NULL;
 
172
    if (code) {
 
173
        const char *sep_pos = strpbrk(code, separator);
 
174
 
 
175
        if (!sep_pos) {
 
176
            if (!code[0]) {                         // end of code
 
177
                token = NULL;
 
178
                code  = NULL;
 
179
            }
 
180
            else {
 
181
                token = strdup(code);
 
182
                code  = NULL;
 
183
            }
 
184
        }
 
185
        else {
 
186
            token = GB_strpartdup(code, sep_pos-1);
 
187
 
 
188
            const char *behind_sep = sep_pos + strspn(sep_pos, separator); // next non 'separator' char
 
189
            if (lineno) {
 
190
                int no_of_linefeeds = 0;
 
191
                while (code<behind_sep) if (*++code == '\n') ++no_of_linefeeds;
 
192
 
 
193
                *lineno += no_of_linefeeds;
 
194
            }
 
195
            else {
 
196
                code = behind_sep;
 
197
            }
 
198
        }
 
199
    }
 
200
    return token;
 
201
}
 
202
 
 
203
inline char *get_token(const char*& code, const char *separator) {
 
204
    return get_token_and_incr_lineno(code, separator, NULL);
 
205
}
 
206
 
 
207
inline bool is_ID_char(char c)  { return isalnum(c) || c == '_'; }
 
208
 
 
209
inline const char *next_closing_paren(const char *code) {
 
210
    const char *open_paren  = strchr(code, '(');
 
211
    const char *close_paren = strchr(code, ')');
 
212
 
 
213
    if (!open_paren || (close_paren && close_paren<open_paren)) return close_paren;
 
214
 
 
215
    close_paren = next_closing_paren(open_paren+1);
 
216
    return next_closing_paren(close_paren+1);
 
217
}
 
218
 
 
219
inline const char *next_comma_outside_parens(const char *code) {
 
220
    const char *comma = strchr(code, ',');
 
221
    if (comma) {
 
222
        const char *open_paren = strchr(code, '(');
 
223
        if (open_paren && open_paren<comma) {
 
224
            const char *close_paren = next_closing_paren(open_paren+1);
 
225
            if (!close_paren) throw "Unbalanced parenthesis";
 
226
            comma = next_comma_outside_parens(close_paren+1);
 
227
        }
 
228
    }
 
229
    return comma;
 
230
}
 
231
 
 
232
inline bool find_open_close_paren(const char *code, size_t& opening_paren_pos, size_t& closing_paren_pos) {
 
233
    const char *open_paren = strchr(code, '(');
 
234
    if (open_paren) {
 
235
        const char *close_paren = next_closing_paren(open_paren+1);
 
236
        if (close_paren) {
 
237
            opening_paren_pos = open_paren-code;
 
238
            closing_paren_pos = close_paren-code;
 
239
            return true;
 
240
        }
 
241
    }
 
242
    return false;
 
243
}
 
244
 
 
245
inline string concat_type_and_name(const string& type, const string& name) {
 
246
    if (type.at(type.length()-1) == '*') return type+name;
 
247
    return type+' '+name;
 
248
}
 
249
 
 
250
// ----------------
 
251
//      TypeMap
 
252
 
 
253
class TypeMap {
 
254
    // representation of types mapped in 'typemap' file
 
255
    set<string> defined_types;
 
256
 
 
257
public:
 
258
    TypeMap() {}
 
259
 
 
260
    void load(LineReader& typemap);
 
261
    bool has_definition_for(const string& type_decl) const {
 
262
        return defined_types.find(type_decl) != defined_types.end();
 
263
    }
 
264
};
 
265
 
 
266
// -------------
 
267
//      Type
 
268
 
 
269
enum TypeClass {
 
270
    INVALID_TYPE,                                   // invalid
 
271
 
 
272
    VOID,                                           // no parameter
 
273
    SIMPLE_TYPE,                                    // simple types like int, float, double, ...
 
274
    CONST_CHAR,                                     // 'const char *'
 
275
    HEAP_COPY,                                      // type is 'char*' and interpreted as heap-copy
 
276
    CONVERSION_FUNCTION,                            // convert type using GBP_-conversion functions
 
277
    TYPEMAPPED,                                     // type is defined in file 'typemap'
 
278
 
 
279
    CANT_HANDLE,                                    // type cannot be used in perl interface
 
280
    FORBIDDEN,                                      // usage forbidden via 'NOT4PERL'
 
281
};
 
282
 
 
283
#if defined(TRACE)
 
284
#define TypeClass2CSTR(type) case type: return #type
 
285
const char *get_TypeClass_name(TypeClass type_class) {
 
286
    switch (type_class) {
 
287
        TypeClass2CSTR(INVALID_TYPE);
 
288
        TypeClass2CSTR(VOID);
 
289
        TypeClass2CSTR(SIMPLE_TYPE);
 
290
        TypeClass2CSTR(CONST_CHAR);
 
291
        TypeClass2CSTR(HEAP_COPY);
 
292
        TypeClass2CSTR(CONVERSION_FUNCTION);
 
293
        TypeClass2CSTR(TYPEMAPPED);
 
294
        TypeClass2CSTR(CANT_HANDLE);
 
295
        TypeClass2CSTR(FORBIDDEN);
 
296
    }
 
297
    return NULL;
 
298
}
 
299
#undef TypeClass2CSTR
 
300
#endif // TRACE
 
301
 
 
302
inline string type2id(const string& type) {
 
303
    char *s = GBS_string_eval(type.c_str(),
 
304
                              "const =:"           // remove const (for less ugly names)
 
305
                              " =:"                // remove spaces
 
306
                              "\\*=Ptr"            // rename '*'
 
307
                              , NULL);
 
308
 
 
309
    string id(s);
 
310
    free(s);
 
311
    return id;
 
312
}
 
313
inline string conversion_function_name(const string& fromType, const string& toType) {
 
314
    string from = type2id(fromType);
 
315
    string to   = type2id(toType);
 
316
    return string("GBP_")+from+"_2_"+to;
 
317
}
 
318
inline string constCastTo(const string& expr, const string& targetType) {
 
319
    return string("const_cast<")+targetType+">("+expr+")";
 
320
}
 
321
 
 
322
class Type { // return- or parameter-type
 
323
    string    c_type;
 
324
    string    perl_type;
 
325
    TypeClass type_class;
 
326
 
 
327
    string unify_type_decl(const char *code) {
 
328
        string type_decl;
 
329
        enum { SPACE, STAR, ID, UNKNOWN } last = SPACE, curr;
 
330
        for (int i = 0; code[i]; ++i) {
 
331
            char c = code[i];
 
332
 
 
333
            switch (c) {
 
334
                case ' ': curr = SPACE; break;
 
335
                case '*': curr = STAR; break;
 
336
                default: curr = is_ID_char(c) ? ID : UNKNOWN; break;
 
337
            }
 
338
 
 
339
            if (last != SPACE && curr != last) type_decl += ' ';
 
340
            if (curr != SPACE)                 type_decl += c;
 
341
 
 
342
            last = curr;
 
343
        }
 
344
 
 
345
        return last == SPACE
 
346
            ? type_decl.substr(0, type_decl.length()-1)
 
347
            : type_decl;
 
348
    }
 
349
 
 
350
    void throw_if_enum() const {
 
351
        size_t enum_pos = c_type.find("enum ");
 
352
        if (enum_pos != string::npos) {
 
353
            const char *enum_type = c_type.c_str()+enum_pos;
 
354
            const char *enum_name = enum_type+5;
 
355
            throw GBS_global_string("do not use '%s', simply use '%s'", enum_type, enum_name);
 
356
        }
 
357
    }
 
358
 
 
359
    string convertExpression(const string& expr, const string& fromType, const string& toType) const {
 
360
        arb_assert(possible_in_xsub());
 
361
        throw_if_enum();
 
362
        if (get_TypeClass() == CONVERSION_FUNCTION) {
 
363
            string conversion_function = conversion_function_name(fromType, toType);
 
364
            return conversion_function+"("+expr+")"; // result is toType
 
365
        }
 
366
        return expr;
 
367
    }
 
368
 
 
369
    bool cant_handle(const string& type_decl) {
 
370
        return
 
371
            strpbrk(type_decl.c_str(), "().*") != NULL         || // function-parameters, pointer-types not handled in ctor
 
372
            type_decl.find("GB_CB")            != string::npos || // some typedef'ed function-parameters
 
373
            type_decl.find("CharPtrArray")     != string::npos ||
 
374
            type_decl.find("StrArray")         != string::npos ||
 
375
            type_decl.find("GB_Link_Follower") != string::npos;
 
376
    }
 
377
 
 
378
    bool is_forbidden(const string& type_decl) {
 
379
        return
 
380
            type_decl.find("NOT4PERL")            != string::npos || // 'NOT4PERL' declares prototype as "FORBIDDEN"
 
381
            type_decl.find("GBQUARK")             != string::npos || // internal information, hide from perl
 
382
            type_decl.find("GB_COMPRESSION_MASK") != string::npos || // internal information, hide from perl
 
383
            type_decl.find("GB_CBUFFER")          != string::npos || // internal ARBDB buffers
 
384
            type_decl.find("GB_BUFFER")           != string::npos; // memory managed by ARBDB
 
385
    }
 
386
 
 
387
 
 
388
public:
 
389
    static TypeMap globalTypemap;
 
390
 
 
391
    Type() : type_class(INVALID_TYPE) {}
 
392
    Type(const char *code) {
 
393
        c_type = unify_type_decl(code);
 
394
 
 
395
        if (c_type == "void") { type_class = VOID; }
 
396
        else if (c_type == CONST_CHAR_PTR ||
 
397
                 c_type == "GB_ERROR"     ||
 
398
                 c_type == "GB_CSTR")
 
399
        {
 
400
            type_class = CONST_CHAR;
 
401
            perl_type  = CHAR_PTR;
 
402
        }
 
403
        else if (c_type == CHAR_PTR) {
 
404
            type_class = HEAP_COPY;
 
405
            perl_type  = c_type;
 
406
        }
 
407
        // [Code-TAG: enum_type_replacement]
 
408
        // for each enum type converted here, you need to support a
 
409
        // conversion function in ../ARBDB/adperl.cxx@enum_conversion_functions
 
410
        else if (c_type == "GB_CASE"        ||
 
411
                 c_type == "GB_CB_TYPE"     ||
 
412
                 c_type == "GB_TYPES"       ||
 
413
                 c_type == "GB_UNDO_TYPE"   ||
 
414
                 c_type == "GB_SEARCH_TYPE" ||
 
415
                 c_type == "GB_alignment_type")
 
416
        {
 
417
            type_class = CONVERSION_FUNCTION;
 
418
            perl_type  = CHAR_PTR;
 
419
        }
 
420
        else if (globalTypemap.has_definition_for(c_type)) {
 
421
            type_class = TYPEMAPPED;
 
422
            perl_type  = c_type;
 
423
        }
 
424
        else if (cant_handle(c_type)) { type_class = CANT_HANDLE; }
 
425
        else if (is_forbidden(c_type)) { type_class = FORBIDDEN; } // Caution: this line catches all '*' types not handled above
 
426
        else {
 
427
            type_class = SIMPLE_TYPE;
 
428
            perl_type  = c_type;
 
429
        }
 
430
    }
 
431
 
 
432
    const string& c_decl() const { return c_type; }
 
433
    const string& perl_decl() const { return perl_type; }
 
434
 
 
435
    TypeClass get_TypeClass() const { return type_class; }
 
436
    bool isVoid() const { return get_TypeClass() == VOID; }
 
437
 
 
438
    bool possible_in_xsub() const { return type_class != CANT_HANDLE && type_class != FORBIDDEN; }
 
439
 
 
440
    string convert_argument_for_C(const string& perl_arg) const {
 
441
        if (perl_decl() == CHAR_PTR) {
 
442
            if (c_decl() == CHAR_PTR) throw "argument of type 'char*' is forbidden";
 
443
            string const_perl_arg = constCastTo(perl_arg, CONST_CHAR_PTR); // ensure C uses 'const char *'
 
444
            return convertExpression(const_perl_arg, CONST_CHAR_PTR, c_decl());
 
445
        }
 
446
        return convertExpression(perl_arg, perl_decl(), c_decl());
 
447
    }
 
448
    string convert_result_for_PERL(const string& c_expr) const {
 
449
        arb_assert(type_class != HEAP_COPY);
 
450
        if (perl_decl() == CHAR_PTR) {
 
451
            string const_c_expr = convertExpression(c_expr, c_decl(), CONST_CHAR_PTR);
 
452
            return constCastTo(const_c_expr, CHAR_PTR);
 
453
        }
 
454
        return convertExpression(c_expr, c_decl(), perl_decl());
 
455
    }
 
456
 
 
457
#if defined(TRACE)
 
458
    void dump_if_impossible_in_xsub(FILE *out) const {
 
459
        if (!possible_in_xsub()) {
 
460
            fprintf(out, "TRACE: - impossible type '%s' (TypeClass='%s')\n",
 
461
                    c_type.c_str(), get_TypeClass_name(type_class));
 
462
        }
 
463
    }
 
464
#endif // TRACE
 
465
 
 
466
};
 
467
 
 
468
TypeMap Type::globalTypemap;
 
469
 
 
470
// ------------------
 
471
//      Parameter
 
472
 
 
473
class Parameter {
 
474
    Type   type;
 
475
    string name;
 
476
 
 
477
    static long nonameCount;
 
478
 
 
479
public:
 
480
    Parameter() {}
 
481
    Parameter(const char *code) {
 
482
        const char *last = strchr(code, 0)-1;
 
483
        while (last[0] == ' ') --last;
 
484
 
 
485
        const char *name_start = last;
 
486
        while (name_start >= code && is_ID_char(name_start[0])) --name_start;
 
487
        name_start++;
 
488
 
 
489
        if (name_start>code) {
 
490
            string type_def(code, name_start-code);
 
491
            name = string(name_start, last-name_start+1);
 
492
            type = Type(type_def.c_str());
 
493
 
 
494
            if (type.possible_in_xsub() && !type.isVoid() && name.empty()) {
 
495
                name = GBS_global_string("noName%li", ++nonameCount);
 
496
            }
 
497
        }
 
498
        else if (strcmp(name_start, "void") == 0) {
 
499
            string no_type(name_start, last-name_start+1);
 
500
            name = "";
 
501
            type = Type(no_type.c_str());
 
502
        }
 
503
        else {
 
504
            throw string("can't parse '")+code+"' (expected 'type name')";
 
505
        }
 
506
    }
 
507
 
 
508
    const string& get_name() const { return name; }
 
509
    const Type& get_type() const { return type; }
 
510
 
 
511
    TypeClass get_TypeClass() const { return get_type().get_TypeClass(); }
 
512
    bool isVoid() const { return get_TypeClass() == VOID; }
 
513
 
 
514
    string perl_typed_param() const { return concat_type_and_name(type.perl_decl(), name); }
 
515
    string c_typed_param   () const { return concat_type_and_name(type.c_decl   (), name); }
 
516
};
 
517
 
 
518
long Parameter::nonameCount = 0;
 
519
 
 
520
// ------------------
 
521
//      Prototype
 
522
 
 
523
typedef vector<Parameter>         Arguments;
 
524
typedef Arguments::const_iterator ArgumentIter;
 
525
 
 
526
class Prototype {
 
527
    Parameter function;                             // return-type + function_name
 
528
    Arguments arguments;
 
529
 
 
530
    void parse_arguments(const char *arg_list) {
 
531
        const char *comma = next_comma_outside_parens(arg_list);
 
532
        if (comma) {
 
533
            {
 
534
                char *first_param = GB_strpartdup(arg_list, comma-1);
 
535
                arguments.push_back(Parameter(first_param));
 
536
                free(first_param);
 
537
            }
 
538
            parse_arguments(comma+1);
 
539
        }
 
540
        else { // only one parameter
 
541
            arguments.push_back(Parameter(arg_list));
 
542
        }
 
543
    }
 
544
 
 
545
public:
 
546
    Prototype(const char *code) {
 
547
        size_t open_paren, close_paren;
 
548
        if (!find_open_close_paren(code, open_paren, close_paren)) {
 
549
            throw "expected parenthesis";
 
550
        }
 
551
 
 
552
        string return_type_and_name(code, open_paren);
 
553
        function = Parameter(return_type_and_name.c_str());
 
554
 
 
555
        string arg_list(code+open_paren+1, close_paren-open_paren-1);
 
556
        parse_arguments(arg_list.c_str());
 
557
    }
 
558
 
 
559
    const Type& get_return_type() const { return function.get_type(); }
 
560
    const string& get_function_name() const { return function.get_name(); }
 
561
 
 
562
    ArgumentIter args_begin() const { return arguments.begin(); }
 
563
    ArgumentIter args_end() const { return arguments.end(); }
 
564
 
 
565
    string argument_names_list() const {
 
566
        string       argument_list;
 
567
        bool         first   = true;
 
568
        ArgumentIter arg_end = arguments.end();
 
569
 
 
570
        for (ArgumentIter param = arguments.begin(); param != arg_end; ++param) {
 
571
            if (!param->isVoid()) {
 
572
                if (first) first    = false;
 
573
                else argument_list += ", ";
 
574
 
 
575
                argument_list += param->get_name();
 
576
            }
 
577
        }
 
578
        return argument_list;
 
579
    }
 
580
 
 
581
    string call_arguments() const {
 
582
        string       argument_list;
 
583
        bool         first   = true;
 
584
        ArgumentIter arg_end = arguments.end();
 
585
 
 
586
        for (ArgumentIter arg = arguments.begin(); arg != arg_end; ++arg) {
 
587
            if (!arg->isVoid()) {
 
588
                if (first) first    = false;
 
589
                else argument_list += ", ";
 
590
 
 
591
                argument_list += arg->get_type().convert_argument_for_C(arg->get_name());
 
592
            }
 
593
        }
 
594
        return argument_list;
 
595
    }
 
596
 
 
597
    bool possible_as_xsub() const {
 
598
        if (get_return_type().possible_in_xsub()) {
 
599
            ArgumentIter arg_end = arguments.end();
 
600
            for (ArgumentIter arg = arguments.begin(); arg != arg_end; ++arg) {
 
601
                if (!arg->get_type().possible_in_xsub()) {
 
602
                    return false;
 
603
                }
 
604
            }
 
605
            return true;
 
606
        }
 
607
        return false;
 
608
    }
 
609
 
 
610
#if defined(TRACE)
 
611
    void dump_types_impossible_in_xsub(FILE *out) const {
 
612
        get_return_type().dump_if_impossible_in_xsub(out);
 
613
        ArgumentIter arg_end = arguments.end();
 
614
        for (ArgumentIter arg = arguments.begin(); arg != arg_end; ++arg) {
 
615
            arg->get_type().dump_if_impossible_in_xsub(out);
 
616
        }
 
617
    }
 
618
#endif // TRACE
 
619
};
 
620
 
 
621
inline void trim(string& text) {
 
622
    const char *whiteSpace = " \t";
 
623
    size_t      leading    = text.find_first_not_of(whiteSpace);
 
624
    size_t      trailing   = text.find_last_not_of(whiteSpace, leading);
 
625
 
 
626
    if (trailing != string::npos) {
 
627
        text = text.substr(leading, text.length()-leading-trailing);
 
628
    }
 
629
}
 
630
 
 
631
void TypeMap::load(LineReader& typemap_reader) {
 
632
    string line;
 
633
    while (typemap_reader.getLine(line)) {
 
634
        if (line == "TYPEMAP") {
 
635
            while (typemap_reader.getLine(line)) {
 
636
                trim(line);
 
637
                if (!line.empty()) {
 
638
                    Parameter     typemapping(line.c_str());
 
639
                    const string& c_type = typemapping.get_type().c_decl();
 
640
                    defined_types.insert(c_type);
 
641
                }
 
642
            }
 
643
            return;
 
644
        }
 
645
    }
 
646
 
 
647
    throw InputFileError(typemap_reader, "Expected to see 'TYPEMAP'");
 
648
}
 
649
 
 
650
// ----------------
 
651
//      Package
 
652
 
 
653
class Package : virtual Noncopyable {
 
654
    string         prefix;                          // e.g. 'P2A' or 'P2AT'
 
655
    string         name;                            // e.g. 'ARB' or 'BIO'
 
656
    GBS_strstruct *generated_code;
 
657
    GB_HASH       *functions_to_skip;
 
658
 
 
659
public:
 
660
    Package(const char *name_, const char *prefix_)
 
661
        : prefix(prefix_)
 
662
        , name(name_)
 
663
    {
 
664
        generated_code    = GBS_stropen(100000);
 
665
        functions_to_skip = GBS_create_hash(1000, GB_MIND_CASE);
 
666
    }
 
667
    ~Package() {
 
668
        GBS_free_hash(functions_to_skip);
 
669
        GBS_strforget(generated_code);
 
670
    }
 
671
 
 
672
    bool matches_package_prefix(const string& text) const { return text.find(prefix) == 0 && text.at(prefix.length()) == '_'; }
 
673
 
 
674
    void mark_function_defined(const string& function) { GBS_write_hash(functions_to_skip, function.c_str(), 1); }
 
675
    bool not_defined(const string& function) const { return GBS_read_hash(functions_to_skip, function.c_str()) == 0; }
 
676
 
 
677
    const string& get_prefix() const { return prefix; }
 
678
 
 
679
    void append_code(const string& code) { GBS_strncat(generated_code, code.c_str(), code.length()); }
 
680
    void append_code(const char *code) { append_code(string(code)); }
 
681
    void append_code(char code) { GBS_chrcat(generated_code, code); }
 
682
 
 
683
    void append_linefeed(size_t count = 1) { while (count--) append_code("\n"); }
 
684
 
 
685
    void print_xsubs(FILE *file) {
 
686
        fputs("# --------------------------------------------------------------------------------\n", file);
 
687
        fprintf(file, "MODULE = ARB  PACKAGE = %s  PREFIX = %s_\n\n", name.c_str(), prefix.c_str());
 
688
        fputs(GBS_mempntr(generated_code), file);
 
689
    }
 
690
};
 
691
 
 
692
// ----------------------
 
693
//      xsubGenerator
 
694
 
 
695
class xsubGenerator {
 
696
    Package arb;
 
697
    Package bio;
 
698
 
 
699
    void generate_xsub(const Prototype& prototype);
 
700
public:
 
701
    xsubGenerator()
 
702
        : arb("ARB", "P2A")
 
703
        , bio("BIO", "P2AT")
 
704
    {}
 
705
 
 
706
    void mark_handcoded_functions(BufferedFileReader& handcoded) {
 
707
        string line;
 
708
        while (handcoded.getLine(line)) {
 
709
            Package *package = NULL;
 
710
 
 
711
            if      (arb.matches_package_prefix(line)) package = &arb;
 
712
            else if (bio.matches_package_prefix(line)) package = &bio;
 
713
 
 
714
            if (package) {
 
715
                size_t open_paren = line.find('(');
 
716
                if (open_paren != string::npos) {
 
717
                    package->mark_function_defined(line.substr(0, open_paren));
 
718
                }
 
719
            }
 
720
 
 
721
        }
 
722
        handcoded.rewind();
 
723
    }
 
724
 
 
725
    void generate_all_xsubs(LineReader& prototype_reader);
 
726
 
 
727
    void print_xsubs(FILE *out) {
 
728
        arb.print_xsubs(out);
 
729
        bio.print_xsubs(out);
 
730
    }
 
731
};
 
732
 
 
733
inline string prefix_before(const string& name, char separator) {
 
734
    size_t sep_offset = name.find_first_of(separator);
 
735
    if (sep_offset != string::npos) {
 
736
        return name.substr(0, sep_offset);
 
737
    }
 
738
    return "";
 
739
}
 
740
 
 
741
inline void GBS_spaces(GBS_strstruct *out, int space_count) {
 
742
    const char *spaces = "          ";
 
743
    arb_assert(space_count <= 10);
 
744
    GBS_strncat(out, spaces+(10-space_count), space_count);
 
745
}
 
746
 
 
747
void xsubGenerator::generate_xsub(const Prototype& prototype) {
 
748
    const string&  c_function_name = prototype.get_function_name();
 
749
    string         function_prefix = prefix_before(c_function_name, '_');
 
750
    Package       *package         = NULL;
 
751
 
 
752
    if (function_prefix == "GB" || function_prefix == "GBC") {
 
753
        package = &arb;
 
754
    }
 
755
    else if (function_prefix == "GBT" || function_prefix == "GEN") {
 
756
        package = &bio;
 
757
    }
 
758
 
 
759
    if (package) {
 
760
        string perl_function_name = package->get_prefix() + c_function_name.substr(function_prefix.length());
 
761
 
 
762
        if (package->not_defined(perl_function_name)) {
 
763
            package->mark_function_defined(perl_function_name); // do NOT xsub functions twice
 
764
 
 
765
            // generate xsub function header
 
766
            const Type& return_type = prototype.get_return_type();
 
767
            {
 
768
                string argument_names_list = prototype.argument_names_list();
 
769
                string function_header     = return_type.isVoid() ? "void" : return_type.perl_decl();
 
770
 
 
771
                function_header += '\n';
 
772
                function_header += perl_function_name+'('+argument_names_list+")\n";
 
773
 
 
774
                ArgumentIter arg_end = prototype.args_end();
 
775
                for (ArgumentIter arg = prototype.args_begin(); arg != arg_end; ++arg) {
 
776
                    if (!arg->isVoid()) {
 
777
                        string type_decl = string("    ") + arg->perl_typed_param() + '\n';
 
778
                        function_header += type_decl;
 
779
                    }
 
780
                }
 
781
 
 
782
                package->append_code(function_header);
 
783
                package->append_linefeed();
 
784
            }
 
785
 
 
786
            // generate xsub function body
 
787
            string call_c_function = c_function_name+'('+prototype.call_arguments()+")";
 
788
            if (return_type.isVoid()) {
 
789
                package->append_code("  PPCODE:\n    ");
 
790
                package->append_code(call_c_function);
 
791
                package->append_code(';');
 
792
            }
 
793
            else {
 
794
                string assign_RETVAL = "    ";
 
795
 
 
796
                switch (return_type.get_TypeClass()) {
 
797
                    case CONST_CHAR:
 
798
                    case CONVERSION_FUNCTION:
 
799
                    case SIMPLE_TYPE:
 
800
                    case TYPEMAPPED:
 
801
                        assign_RETVAL = string("    RETVAL = ") + return_type.convert_result_for_PERL(call_c_function);
 
802
                        break;
 
803
 
 
804
                    case HEAP_COPY:
 
805
                        // temporarily store heapcopy in static pointer
 
806
                        // defined at ../PERL2ARB/ARB_ext.c@static_pntr
 
807
                        assign_RETVAL =
 
808
                            string("    freeset(static_pntr, ") + call_c_function+");\n"+
 
809
                            "    RETVAL = static_pntr";
 
810
                        break;
 
811
 
 
812
                    case VOID:
 
813
                    case INVALID_TYPE:
 
814
                    case CANT_HANDLE:
 
815
                    case FORBIDDEN:
 
816
                        arb_assert(0);
 
817
                }
 
818
 
 
819
                string body =
 
820
                    string("  CODE:\n") +
 
821
                    assign_RETVAL + ";\n" +
 
822
                    "\n" +
 
823
                    "  OUTPUT:\n" +
 
824
                    "    RETVAL";
 
825
 
 
826
                package->append_code(body);
 
827
            }
 
828
            package->append_linefeed(3);
 
829
        }
 
830
#if defined(TRACE)
 
831
        else {
 
832
            fprintf(stderr, "TRACE: '%s' skipped\n", c_function_name.c_str());
 
833
        }
 
834
#endif // TRACE
 
835
    }
 
836
#if defined(TRACE)
 
837
    else {
 
838
        fprintf(stderr, "TRACE: Skipped function: '%s' (prefix='%s')\n", c_function_name.c_str(), function_prefix.c_str());
 
839
    }
 
840
#endif // TRACE
 
841
}
 
842
 
 
843
static void print_prototype_parse_error(LineReader& prototype_reader, const char *err, const char *prototype) {
 
844
    InputFileError(prototype_reader, GBS_global_string("%s (can't xsub '%s')", err, prototype)).print();
 
845
}
 
846
 
 
847
void xsubGenerator::generate_all_xsubs(LineReader& prototype_reader) {
 
848
    bool   error_occurred     = false;
 
849
    string line;
 
850
    int    open_brace_counter = 0;
 
851
 
 
852
    while (prototype_reader.getLine(line)) {
 
853
        const char *lineStart          = line.c_str();
 
854
        size_t      leading_whitespace = strspn(lineStart, " \t");
 
855
        const char *prototype          = lineStart+leading_whitespace;
 
856
 
 
857
        if (!open_brace_counter && is_prototype(prototype)) {
 
858
            try {
 
859
                Prototype proto(prototype);
 
860
                if (proto.possible_as_xsub()) {
 
861
                    generate_xsub(proto);
 
862
                }
 
863
#if defined(TRACE)
 
864
                else {
 
865
                    fprintf(stderr, "TRACE: prototype '%s' not possible as xsub\n", prototype);
 
866
                    proto.dump_types_impossible_in_xsub(stderr);
 
867
                }
 
868
#endif // TRACE
 
869
            }
 
870
            catch(string& err) {
 
871
                print_prototype_parse_error(prototype_reader, err.c_str(), prototype);
 
872
                error_occurred = true;
 
873
            }
 
874
            catch(const char *err) {
 
875
                print_prototype_parse_error(prototype_reader, err, prototype);
 
876
                error_occurred = true;
 
877
            }
 
878
            catch(...) { arb_assert(0); }
 
879
        }
 
880
        else {
 
881
#if defined(TRACE)
 
882
            fprintf(stderr, "TRACE: not a prototype: '%s'\n", prototype);
 
883
#endif // TRACE
 
884
            trace_over_braces(prototype, open_brace_counter);
 
885
        }
 
886
    }
 
887
 
 
888
    if (error_occurred) throw ProgramError("could not generate xsubs for all prototypes");
 
889
}
 
890
 
 
891
static void print_xs_default(BufferedFileReader& xs_default, const char *proto_filename, FILE *out) {
 
892
    fprintf(out,
 
893
            "/* This file has been generated from\n"
 
894
            " *    %s and\n"
 
895
            " *    %s\n */\n"
 
896
            "\n",
 
897
           xs_default.getFilename().c_str(),
 
898
           proto_filename);
 
899
 
 
900
    xs_default.copyTo(out);
 
901
    xs_default.rewind();
 
902
}
 
903
 
 
904
static BufferedFileReader *createFileBuffer(const char *filename) {
 
905
    FILE *in = fopen(filename, "rt");
 
906
    if (!in) {
 
907
        GB_export_IO_error("reading", filename);
 
908
        throw ProgramError(GB_await_error());
 
909
    }
 
910
    return new BufferedFileReader(filename, in);
 
911
}
 
912
static BufferedFileReader *createCommentSkippingFileBuffer(const char *filename) {
 
913
    FILE *in = fopen(filename, "rt");
 
914
    if (!in) {
 
915
        GB_export_IO_error("reading", filename);
 
916
        throw ProgramError(GB_await_error());
 
917
    }
 
918
    return new CommentSkippingFileBuffer(filename, in, "/*", "*/", "//");
 
919
}
 
920
 
 
921
 
 
922
 
 
923
static void loadTypemap(const char *typemap_filename) {
 
924
    SmartPtr<BufferedFileReader> typemap = createFileBuffer(typemap_filename);
 
925
    Type::globalTypemap.load(*typemap);
 
926
}
 
927
 
 
928
int ARB_main(int argc, char **argv)
 
929
{
 
930
    bool error_occurred = false;
 
931
    try {
 
932
        if (argc != 4) {
 
933
            fputs("arb_proto_2_xsub converts GB_prototypes for the ARB perl interface\n"
 
934
                  "Usage: arb_proto_2_xsub <prototypes.h> <xs-header> <typemap>\n"
 
935
                  "       <prototypes.h> contains prototypes of ARBDB library\n"
 
936
                  "       <xs-header>    may contain prototypes, which will not be\n"
 
937
                  "                      overwritten by generated default prototypes\n"
 
938
                  "       <typemap>      contains type-conversion-definitions, which can\n"
 
939
                  "                      be handled by xsubpp\n"
 
940
                  , stderr);
 
941
 
 
942
            throw ProgramError("Wrong number of command line arguments");
 
943
        }
 
944
        else {
 
945
            const char *proto_filename   = argv[1];
 
946
            const char *xs_default_name  = argv[2];
 
947
            const char *typemap_filename = argv[3];
 
948
 
 
949
            loadTypemap(typemap_filename);
 
950
 
 
951
            // generate xsubs
 
952
            SmartPtr<BufferedFileReader> xs_default = createFileBuffer(xs_default_name);
 
953
 
 
954
            xsubGenerator generator;
 
955
            generator.mark_handcoded_functions(*xs_default);
 
956
            {
 
957
                SmartPtr<BufferedFileReader> prototypes = createCommentSkippingFileBuffer(proto_filename);
 
958
                generator.generate_all_xsubs(*prototypes);
 
959
            }
 
960
 
 
961
            // write xsubs
 
962
            FILE *out = stdout;
 
963
            print_xs_default(*xs_default, proto_filename, out);
 
964
            generator.print_xsubs(out);
 
965
        }
 
966
    }
 
967
    catch (Error& err) {
 
968
        err.print();
 
969
        error_occurred = true;
 
970
    }
 
971
    catch (...) {
 
972
        ProgramError("Unexpected exception").print();
 
973
        error_occurred = true;
 
974
    }
 
975
 
 
976
    if (error_occurred) {
 
977
        ProgramError("failed").print();
 
978
        return EXIT_FAILURE;
 
979
    }
 
980
    return EXIT_SUCCESS;
 
981
}
 
982
 
 
983
 
 
984
// --------------------------------------------------------------------------------
 
985
 
 
986
#ifdef UNIT_TESTS
 
987
#ifndef TEST_UNIT_H
 
988
#include <test_unit.h>
 
989
#endif
 
990
#include <ut_valgrinded.h>
 
991
 
 
992
// #define TEST_AUTO_UPDATE // uncomment this to update expected results
 
993
 
 
994
inline GB_ERROR valgrinded_system(const char *cmdline) {
 
995
    char *cmddup = strdup(cmdline);
 
996
    make_valgrinded_call(cmddup);
 
997
 
 
998
    GB_ERROR error = GBK_system(cmddup);
 
999
    free(cmddup);
 
1000
    return error;
 
1001
}
 
1002
 
 
1003
void TEST_arb_proto_2_xsub() {
 
1004
    TEST_EXPECT_ZERO(chdir("xsub"));
 
1005
 
 
1006
    const char *outname  = "ap2x.out";
 
1007
    const char *expected = "ap2x.out.expected";
 
1008
 
 
1009
    char *cmd = GBS_global_string_copy("arb_proto_2_xsub ptype.header default.xs typemap > %s", outname);
 
1010
    TEST_EXPECT_NO_ERROR(valgrinded_system(cmd));
 
1011
 
 
1012
#if defined(TEST_AUTO_UPDATE)
 
1013
    system(GBS_global_string("cp %s %s", outname, expected));
 
1014
#else
 
1015
    TEST_EXPECT_TEXTFILE_DIFFLINES(expected, outname, 0);
 
1016
#endif
 
1017
    TEST_EXPECT_ZERO_OR_SHOW_ERRNO(unlink(outname));
 
1018
 
 
1019
    free(cmd);
 
1020
}
 
1021
 
 
1022
#endif // UNIT_TESTS
 
1023
 
 
1024
// --------------------------------------------------------------------------------