source: tags/svn.1.5.4/ARBDB/adlang1.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: 91.1 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : adlang1.cxx                                       //
4//   Purpose   :                                                   //
5//                                                                 //
6//   Institute of Microbiology (Technical University Munich)       //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11#include "gb_aci.h"
12#include "gb_key.h"
13#include "gb_localdata.h"
14
15#include <adGene.h>
16#include <arbdbt.h>
17
18#include <static_assert.h>
19#include <arb_defs.h>
20#include <arb_strbuf.h>
21#include <arb_file.h>
22#include <aw_awar_defs.hxx>
23
24#include <cctype>
25
26// hook for 'export_sequence'
27
28static gb_export_sequence_cb get_export_sequence = 0;
29
30NOT4PERL void GB_set_export_sequence_hook(gb_export_sequence_cb escb) {
31    gb_assert(!get_export_sequence || !escb); // avoid unwanted overwrite
32    get_export_sequence = escb;
33}
34
35// global ACI/SRT debug switch
36static int trace = 0;
37
38static void GB_set_ACISRT_trace(int enable) { trace = enable; }
39int GB_get_ACISRT_trace() { return trace; }
40
41
42// export stream
43
44#define PASS_2_OUT(args, s)  do { (*((args)->voutput))[(*((args)->coutput))++].str = (s); } while (0)
45#define COPY_2_OUT(args, s)  PASS_2_OUT(args, strdup(s));
46#define IN_2_OUT(args, i)    COPY_2_OUT(args, args->vinput[i].str);
47
48// ----------------------------
49//      Parameter functions
50
51struct gbl_param {
52    gbl_param  *next;
53    GB_TYPES    type;                               // type of variable
54    void       *varaddr;                            // address of variable where value gets stored
55    const char *param_name;                         // parameter name (e.g. 'include=')
56    const char *help_text;                          // help text for parameter
57};
58
59#define GBL_BEGIN_PARAMS gbl_param *params = 0
60
61static void gbl_new_param(gbl_param **pp, GB_TYPES type, void *vaddr, const char *param_name, const char *help_text) {
62    gbl_param *gblp = (gbl_param *)GB_calloc(1, sizeof(gbl_param));
63
64    gblp->next = *pp;
65    *pp         = gblp;
66
67    gblp->type       = type;
68    gblp->varaddr    = vaddr;
69    gblp->param_name = param_name;
70    gblp->help_text  = help_text;
71}
72
73typedef const char   *String;
74typedef int           bit;
75typedef unsigned int  uint;
76
77static int gbl_param_int(const char *param_name, int def, const char *help_text, gbl_param **pp, int *vaddr) {
78    gbl_new_param(pp, GB_INT, vaddr, param_name, help_text);
79    return def;
80}
81
82static char gbl_param_char(const char *param_name, char def, const char *help_text, gbl_param **pp, char *vaddr) {
83    gbl_new_param(pp, GB_BYTE, vaddr, param_name, help_text);
84    return def;
85}
86
87static uint gbl_param_uint(const char *param_name, uint def, const char *help_text, gbl_param **pp, uint *vaddr) {
88    gbl_new_param(pp, GB_INT, vaddr, param_name, help_text);
89    return def;
90}
91
92static const char *gbl_param_String(const char *param_name, const char *def, const char *help_text, gbl_param **pp, String *vaddr) {
93    gbl_new_param(pp, GB_STRING, vaddr, param_name, help_text);
94    return def;
95}
96
97static int gbl_param_bit(const char *param_name, int def, const char *help_text, gbl_param **pp, bit *vaddr) {
98    gbl_new_param(pp, GB_BIT, vaddr, param_name, help_text);
99    return def;
100}
101
102#define GBL_PARAM_TYPE(type, var, param_name, def, help_text) type  var = gbl_param_##type(param_name, def, help_text, &params, &var)
103#define GBL_STRUCT_PARAM_TYPE(type, strct, member, param_name, def, help_text) strct.member = gbl_param_##type(param_name, def, help_text, &params, &strct.member)
104
105#define GBL_PARAM_INT(var,    param_name, def, help_text) GBL_PARAM_TYPE(int,    var, param_name, def, help_text)
106#define GBL_PARAM_CHAR(var,   param_name, def, help_text) GBL_PARAM_TYPE(char,   var, param_name, def, help_text)
107#define GBL_PARAM_UINT(var,   param_name, def, help_text) GBL_PARAM_TYPE(uint,   var, param_name, def, help_text)
108#define GBL_PARAM_STRING(var, param_name, def, help_text) GBL_PARAM_TYPE(String, var, param_name, def, help_text)
109#define GBL_PARAM_BIT(var,    param_name, def, help_text) GBL_PARAM_TYPE(bit,    var, param_name, def, help_text)
110
111#define GBL_STRUCT_PARAM_INT(strct,    member, param_name, def, help_text) GBL_STRUCT_PARAM_TYPE(int,    strct, member, param_name, def, help_text)
112#define GBL_STRUCT_PARAM_CHAR(strct,   member, param_name, def, help_text) GBL_STRUCT_PARAM_TYPE(char,   strct, member, param_name, def, help_text)
113#define GBL_STRUCT_PARAM_UINT(strct,   member, param_name, def, help_text) GBL_STRUCT_PARAM_TYPE(uint,   strct, member, param_name, def, help_text)
114#define GBL_STRUCT_PARAM_STRING(strct, member, param_name, def, help_text) GBL_STRUCT_PARAM_TYPE(String, strct, member, param_name, def, help_text)
115#define GBL_STRUCT_PARAM_BIT(strct,    member, param_name, def, help_text) GBL_STRUCT_PARAM_TYPE(bit,    strct, member, param_name, def, help_text)
116
117#define GBL_TRACE_PARAMS(argc, argv)                                    \
118    do {                                                                \
119        GB_ERROR def_error =                                            \
120            trace_params(argc, argv, params, args->command);            \
121        if (def_error) { GBL_END_PARAMS; return def_error; }            \
122    } while(0)
123
124#define GBL_END_PARAMS                                                  \
125    do {                                                                \
126        gbl_param *_gblp;                                               \
127        while (params) {                                                \
128            _gblp = params;                                             \
129            params = params->next;                                      \
130            free(_gblp);                                                \
131        }                                                               \
132    } while (0)
133
134#define GBL_CHECK_FREE_PARAM(nr, cnt)           \
135    do {                                        \
136        if ((nr)+(cnt) >= GBL_MAX_ARGUMENTS) {  \
137            /* gb_assert(0); */                 \
138            return "max. parameters exceeded";  \
139        }                                       \
140    } while (0)
141
142#if defined(WARN_TODO)
143#warning remove GBL_MAX_ARGUMENTS - instead allocate dynamic
144#endif
145
146
147static GB_ERROR trace_params(int argc, const GBL *argv, gbl_param *ppara, const char *com) {
148    GB_ERROR error = 0;
149    int      i;
150
151    for (i=0; i<argc; i++) {
152        gbl_param  *para;
153        const char *argument = argv[i].str;
154
155        for (para = ppara; para && !error; para = para->next) {
156            int len = strlen(para->param_name);
157
158            if (strncmp(argument, para->param_name, len) == 0) {
159                const char *value = argument+len; // set to start of value
160
161                if (para->type == GB_BIT) {
162                    // GB_BIT is special cause param_name does NOT contain trailing '='
163
164                    if (!value[0]) { // only 'name' -> handle like 'name=1'
165                        ;
166                    }
167                    else if (value[0] == '=') {
168                        value++;
169                    }
170                }
171
172                switch (para->type) {
173                    case GB_STRING:
174                        *(const char **)para->varaddr  = value;
175                        break;
176
177                    case GB_INT:
178                        COMPILE_ASSERT(sizeof(int) == sizeof(uint)); // assumed by GBL_PARAM_UINT
179                        *(int *)para->varaddr = atoi(value);
180                        break;
181
182                    case GB_BIT:
183                        // 'param=' is same as 'param' or 'param=1' (historical reason, don't change)
184                        *(int *)para->varaddr = (value[0] ? atoi(value) : 1);
185                        break;
186
187                    case GB_BYTE:
188                        *(char *)para->varaddr = *value; // this may use the terminal zero-byte (e.g. for p1 in 'p0=0,p1=,p2=2' )
189                        if (value[0] && value[1]) { // found at least 2 chars
190                            GB_warningf("Only one character expected in value '%s' of param '%s' - rest is ignored", value, para->param_name);
191                        }
192                        break;
193
194                    default:
195                        gb_assert(0);
196                        error = GBS_global_string("Parameter '%s': Unknown type %i (internal error)", para->param_name, para->type);
197                        break;
198                }
199                break; // accept parameter
200            }
201        }
202
203        if (!error && !para) { // no parameter found for argument
204            int             pcount = 0;
205            gbl_param     **params;
206            int             k;
207            char           *res;
208            GBS_strstruct  *str    = GBS_stropen(1000);
209            GB_ERROR        err;
210
211
212            for (para = ppara; para; para = para->next) pcount++;
213            params = (gbl_param **)GB_calloc(sizeof(void *), pcount);
214            for (k = 0, para = ppara; para; para = para->next) params[k++] = para;
215
216
217            for (pcount--; pcount>=0; pcount--) {
218                para = params[pcount];
219                GBS_strcat(str, "\t");
220                GBS_strcat(str, para->param_name);
221                switch (para->type) {
222                    case GB_STRING: GBS_strcat(str, "STRING"); break;
223                    case GB_INT:    GBS_strcat(str, "INT");    break;
224                    case GB_FLOAT:  GBS_strcat(str, "FLOAT");  break;
225                    case GB_BYTE:   GBS_strcat(str, "CHAR");   break;
226                    case GB_BIT:    GBS_strcat(str, "    ");   break;
227                    default:        gb_assert (0); GBS_strcat(str, "????"); break;
228                }
229                GBS_strcat(str, "\t\t;");
230                GBS_strcat(str, para->help_text);
231                GBS_strcat(str, "\n");
232            }
233            freenull(params);
234            res = GBS_strclose(str);
235            err = GB_export_errorf("Unknown Parameter '%s' in command '%s'\n\tPARAMETERS:\n%s", argv[i].str, com, res);
236            free(res);
237            return err;
238        }
239    }
240
241    return error;
242}
243
244
245// -------------------------
246//      String functions
247
248static char *unEscapeString(const char *escapedString) {
249    // replaces all \x by x
250    char *result = nulldup(escapedString);
251    char *to     = result;
252    char *from   = result;
253
254    while (1) {
255        char c = *from++;
256        if (!c) break;
257
258        if (c=='\\') {
259            *to++ = *from++;
260        }
261        else {
262            *to++ = c;
263        }
264    }
265    *to = 0;
266    return result;
267}
268
269static int gbl_stricmp(const char *s1, const char *s2) {
270    // case insensitive strcmp
271    int i;
272    for (i = 0; ; ++i) {
273        char c1 = tolower(s1[i]);
274        char c2 = tolower(s2[i]);
275
276        if (c1 == c2) {
277            if (!c1) break; // equal strings
278        }
279        else {
280            if (c1<c2) return -1;
281            return 1;
282        }
283    }
284    return 0;
285}
286static int gbl_strincmp(const char *s1, const char *s2, int size2) {
287    // case insensitive strcmp
288    int i;
289    for (i = 0; i<size2; ++i) {
290        char c1 = tolower(s1[i]);
291        char c2 = tolower(s2[i]);
292
293        if (c1 == c2) {
294            if (!c1) break; // equal strings
295        }
296        else {
297            if (c1<c2) return -1;
298            return 1;
299        }
300    }
301    return 0;
302}
303static const char *gbl_stristr(const char *haystack, const char *needle) {
304    // case insensitive strstr
305    const char *hp          = haystack;
306    char        c1          = toupper(needle[0]);
307    char        c2          = tolower(c1);
308    int         needle_size = strlen(needle);
309
310    if (c1 == c2) {
311        hp = strchr(hp, c1);
312        while (hp) {
313            if (gbl_strincmp(hp, needle, needle_size) == 0) return hp;
314            hp = strchr(hp+1, c1);
315        }
316    }
317    else {
318        while (hp) {
319            const char *h1 = strchr(hp, c1);
320            const char *h2 = strchr(hp, c2);
321
322            if (h1 && h2) {
323                if (h1<h2) {
324                    if (gbl_strincmp(h1, needle, needle_size) == 0) return h1;
325                    hp = h1+1;
326                }
327                else {
328                    gb_assert(h1>h2);
329                    if (gbl_strincmp(h2, needle, needle_size) == 0) return h2;
330                    hp = h2+1;
331                }
332            }
333            else {
334                if (h1) { hp = h1; }
335                else if (h2) { hp = h2; c1 = c2; }
336                else { hp = 0; }
337
338                while (hp) {
339                    if (gbl_strincmp(hp, needle, needle_size) == 0) return hp;
340                    hp = strchr(hp+1, c1);
341                }
342            }
343        }
344    }
345    return 0;
346}
347
348static GB_ERROR gbl_mid_streams(int arg_cinput, const GBL *arg_vinput,
349                                int *arg_cout, GBL **arg_vout,
350                                int start, int mstart, int end, int relend)
351{
352    // used as well to copy all streams (e.g. by 'dd')
353    int i;
354    GBL_CHECK_FREE_PARAM(*arg_cout, arg_cinput);
355    for (i=0; i<arg_cinput; i++) {       // go through all in streams
356        char *p;
357        int c;
358
359        int len;
360        int nstart = start;
361        int nend = end;
362
363        p = arg_vinput[i].str;
364        len = strlen(p);
365        if (nstart<0) nstart = len - mstart;
366        if (nend<0) nend = len - relend;        // check rel len
367
368        if (nstart>len) nstart = len;   // check boundaries
369        if (nstart<0) nstart = 0;
370        if (nend>len) nend = len;
371        if (nend<nstart) nend = nstart;
372
373        c = p[nend];
374        p[nend] = 0;
375        (*arg_vout)[(*arg_cout)++].str = strdup(p+nstart);     // export result string
376        p[nend] = c;
377    }
378    return 0;
379}
380
381static GB_ERROR gbl_trace(GBL_command_arguments *args) {
382    int tmp_trace;
383
384    if (args->cparam!=1) return "syntax: trace(0|1)";
385
386    tmp_trace = atoi(args->vparam[0].str);
387    if (tmp_trace<0 || tmp_trace>1) return GBS_global_string("Illegal value %i to trace", tmp_trace);
388
389    if (tmp_trace != GB_get_ACISRT_trace()) {
390        printf("*** %sctivated ACI trace ***\n", tmp_trace ? "A" : "De-a");
391        GB_set_ACISRT_trace(tmp_trace);
392    }
393
394    return gbl_mid_streams(args->cinput, args->vinput, args->coutput, args->voutput, 0, 0, -1, 0); // copy all streams
395}
396
397/* ---------------------------------------------------------------------------------------
398 * Binary operators work on pairs of values.
399 * Three different operational modes are implemented for all binary operators:
400 *
401 * 1. inputstreams|operator
402 *
403 *    The number of inputstreams has to be even and the operator will be
404 *    applied to pairs of them.
405 *
406 *    Example : a;b;c;d;e;f | plus
407 *    Result  : a+b;c+d;e+f
408 *
409 * 2. inputstreams|operator(x)
410 *
411 *    The number of inputstreams has to be at least 1.
412 *    The operator is applied to each inputstream.
413 *
414 *    Example : a;b;c | plus(d)
415 *    Result  : a+d;b+d;c+d
416 *
417 * 3. operator(x, y)
418 *
419 *    Inputstreams will be ignored and the operator is applied
420 *    to the arguments
421 *
422 *    Example : a;b | plus(c,d)
423 *    Result  : c+d
424 */
425
426typedef char* (*gbl_binary_operator)(const char *arg1, const char *arg2, void *client_data);
427
428static GB_ERROR gbl_apply_binary_operator(GBL_command_arguments *args, gbl_binary_operator op, void *client_data)
429{
430    GB_ERROR  error  = 0;
431
432    switch (args->cparam) {
433        case 0:
434            if (args->cinput == 0) error = "Expect at least two input streams if called with 0 parameters";
435            else if (args->cinput%2) error = "Expect an even number of input streams if called with 0 parameters";
436            else {
437                int inputpairs = args->cinput/2;
438                int i;
439                for (i = 0; i<inputpairs; ++i) {
440                    PASS_2_OUT(args, op(args->vinput[i*2].str, args->vinput[i*2+1].str, client_data));
441                }
442            }
443            break;
444
445        case 1:
446            if (args->cinput == 0) error = "Expect at least one input stream if called with 1 parameter";
447            else {
448                int         i;
449                const char *argument = args->vparam[0].str;
450                for (i = 0; i<args->cinput; ++i) {
451                    PASS_2_OUT(args, op(args->vinput[i].str, argument, client_data));
452                }
453            }
454            break;
455
456        case 2:
457            {
458                GBDATA *gb_main = (GBDATA*)GB_MAIN(args->gb_ref)->data;
459                int     i;
460
461                for (i = 0; i<args->cinput; ++i) {
462                    char *result1       = GB_command_interpreter(gb_main, args->vinput[i].str, args->vparam[0].str, args->gb_ref, args->default_tree_name);
463                    if (!result1) error = GB_await_error();
464                    else {
465                        char *result2       = GB_command_interpreter(gb_main, args->vinput[i].str, args->vparam[1].str, args->gb_ref, args->default_tree_name);
466                        if (!result2) error = GB_await_error();
467                        else {
468                            PASS_2_OUT(args, op(result1, result2, client_data));
469                            free(result2);
470                        }
471                        free(result1);
472                    }
473                }
474
475                break;
476            }
477        default:
478            error = "0 to 2 arguments expected";
479            break;
480    }
481
482    return error;
483}
484
485// ---------------------------------
486//      the commands themselves:
487
488static GB_ERROR gbl_command(GBL_command_arguments *args) {
489    GB_ERROR error = NULL;
490
491    if (args->cparam!=1) {
492        error = "syntax: command(\"escaped command\")";
493    }
494    else {
495        GBDATA *gb_main = (GBDATA*)GB_MAIN(args->gb_ref)->data;
496        int     i;
497        char   *command;
498
499        GBL_CHECK_FREE_PARAM(*args->coutput, args->cinput);
500        command = unEscapeString(args->vparam[0].str);
501#if defined(DEBUG)
502        printf("executing command '%s'\n", command);
503#endif // DEBUG
504
505        for (i=0; i<args->cinput && !error; i++) { // go through all orig streams
506            char *result       = GB_command_interpreter(gb_main, args->vinput[i].str, command, args->gb_ref, args->default_tree_name);
507            if (!result) error = GB_await_error();
508            else  PASS_2_OUT(args, result);
509        }
510        free(command);
511    }
512    return error;
513}
514
515static GB_ERROR gbl_eval(GBL_command_arguments *args) {
516    GB_ERROR error = NULL;
517
518    if (args->cparam!=1) {
519        error = "eval syntax: eval(\"escaped command evaluating to command\")";
520    }
521    else {
522        GBDATA *gb_main = (GBDATA*)GB_MAIN(args->gb_ref)->data;
523        char   *command;
524        char   *to_eval;
525
526        GBL_CHECK_FREE_PARAM(*args->coutput, args->cinput);
527
528        to_eval = unEscapeString(args->vparam[0].str);
529        command = GB_command_interpreter(gb_main, "", to_eval, args->gb_ref, args->default_tree_name); // evaluate independent
530
531        if (!command) error = GB_await_error();
532        else {
533            int i;
534
535            if (GB_get_ACISRT_trace()) {
536                printf("evaluating '%s'\n", to_eval);
537                printf("executing '%s'\n", command);
538            }
539
540            for (i=0; i<args->cinput && !error; i++) { // go through all orig streams
541                char *result       = GB_command_interpreter(gb_main, args->vinput[i].str, command, args->gb_ref, args->default_tree_name);
542                if (!result) error = GB_await_error();
543                else  PASS_2_OUT(args, result);
544            }
545            free(command);
546        }
547        free(to_eval);
548    }
549    return error;
550}
551
552class DefinedCommands : virtual Noncopyable {
553    GB_HASH *cmds;
554public:
555    DefinedCommands() { cmds = GBS_create_dynaval_hash(100, GB_MIND_CASE, GBS_dynaval_free); }
556    ~DefinedCommands() { GBS_free_hash(cmds); }
557
558    void set(const char *name, char* cmd) { GBS_dynaval_free(GBS_write_hash(cmds, name, (long)cmd)); } // takes ownership of 'cmd'!
559    const char *get(const char *name) const { return (const char *)GBS_read_hash(cmds, name); }
560};
561
562static DefinedCommands defined_commands;
563
564static GB_ERROR gbl_define(GBL_command_arguments *args) {
565    GB_ERROR error = 0;
566
567    if (args->cparam!=2) {
568        error = "syntax: define(name, \"escaped command\")";
569    }
570    else {
571        const char *name = args->vparam[0].str;
572        char       *cmd  = unEscapeString(args->vparam[1].str);
573
574        defined_commands.set(name, cmd);
575
576        if (GB_get_ACISRT_trace()) {
577            printf("defining command '%s'='%s'\n", name, cmd);
578        }
579    }
580
581    return error;
582}
583
584static GB_ERROR gbl_do(GBL_command_arguments *args) {
585    GB_ERROR error = 0;
586
587    if (args->cparam!=1) {
588        error = "syntax: do(name)";
589    }
590    else {
591        const char *name = args->vparam[0].str;
592        const char *cmd  = defined_commands.get(name);
593
594        if (!cmd) {
595            error = GBS_global_string("Can't do undefined command '%s' - use define(%s, ...) first", name, name);
596        }
597        else {
598            GBDATA *gb_main = (GBDATA*)GB_MAIN(args->gb_ref)->data;
599            int     i;
600
601            GBL_CHECK_FREE_PARAM(*args->coutput, args->cinput);
602
603            if (GB_get_ACISRT_trace()) {
604                printf("executing defined command '%s'='%s' on %i streams\n", name, cmd, args->cinput);
605            }
606
607            for (i=0; i<args->cinput && !error; i++) { // go through all orig streams
608                char *result       = GB_command_interpreter(gb_main, args->vinput[i].str, cmd, args->gb_ref, args->default_tree_name);
609                if (!result) error = GB_await_error();
610                else  PASS_2_OUT(args, result);
611            }
612        }
613    }
614    return error;
615}
616
617static GB_ERROR gbl_streams(GBL_command_arguments *args) {
618    GBL_CHECK_FREE_PARAM(*args->coutput, args->cinput);
619    PASS_2_OUT(args, GBS_global_string_copy("%i", args->cinput));
620    return 0;
621}
622
623static GB_ERROR gbl_origin(GBL_command_arguments *args) {
624    GB_ERROR error = NULL;
625
626    if (args->cparam!=1) error = GBS_global_string("syntax: %s(\"escaped command\")", args->command);
627    else if (!GEN_is_pseudo_gene_species(args->gb_ref)) {
628        error = GBS_global_string("'%s' applies to gene-species only", args->command);
629    }
630    else {
631        GBDATA *gb_origin = NULL;
632
633        GBL_CHECK_FREE_PARAM(*args->coutput, args->cinput);
634
635        if (strcmp(args->command, "origin_organism") == 0) {
636            gb_origin = GEN_find_origin_organism(args->gb_ref, 0);
637        }
638        else {
639            gb_assert(strcmp(args->command, "origin_gene") == 0);
640            gb_origin = GEN_find_origin_gene(args->gb_ref, 0);
641        }
642
643        if (!error && !gb_origin) error = GB_await_error();
644
645        if (!error) {
646            char   *command = unEscapeString(args->vparam[0].str);
647            int     i;
648            GBDATA *gb_main = (GBDATA*)GB_MAIN(args->gb_ref)->data;
649
650            for (i=0; i<args->cinput && !error; i++) { // go through all orig streams
651                char *result       = GB_command_interpreter(gb_main, args->vinput[i].str, command, gb_origin, args->default_tree_name);
652                if (!result) error = GB_await_error();
653                else  PASS_2_OUT(args, result);
654            }
655
656            free(command);
657        }
658    }
659
660    return error;
661}
662
663static GB_ERROR gbl_count(GBL_command_arguments *args)
664{
665    int  i;
666    char tab[256];              // if tab[char] count 'char'
667
668    if (args->cparam!=1) return "count syntax: count(\"characters to count\")";
669
670    for (i=0; i<256; i++) {
671        if (strchr(args->vparam[0].str, i)) tab[i] = 1;
672        else tab[i] = 0;
673    }
674    GBL_CHECK_FREE_PARAM(*args->coutput, args->cinput);
675    for (i=0; i<args->cinput; i++) {       // go through all orig streams
676        long           sum = 0;                     // count frequencies
677        unsigned char *p   = (unsigned char *)args->vinput[i].str;
678        while (*p) {
679            sum += tab[*(p++)];
680        }
681        PASS_2_OUT(args, GBS_global_string_copy("%li", sum));
682    }
683    return 0;
684}
685
686static GB_ERROR gbl_len(GBL_command_arguments *args)
687{
688    int i;
689    char tab[256];                              // if tab[char] count 'char'
690    const char *option;
691
692    if (args->cparam == 0) option = "";
693    else option = args->vparam[0].str;
694    if (args->cparam>=2) return "len syntax: len[(\"characters not to count\")]";
695
696    for (i = 0; i<256; ++i) tab[i] = 1;
697    for (i = 0; option[i]; ++i) tab[safeCharIndex(option[i])] = 0;
698
699    GBL_CHECK_FREE_PARAM(*args->coutput, args->cinput);
700    for (i=0; i<args->cinput; i++) {       // go through all orig streams
701        char *p;
702        long sum = 0;                   // count frequencies
703        p = args->vinput[i].str;
704        while (*p) {
705            sum += tab[safeCharIndex(*(p++))];
706        }
707        PASS_2_OUT(args, GBS_global_string_copy("%li", sum));
708    }
709    return 0;
710}
711
712static GB_ERROR gbl_remove(GBL_command_arguments *args)
713{
714    int i;
715    char tab[256];                              // if tab[char] count 'char'
716
717
718    if (args->cparam!=1) return "remove syntax: remove(\"characters to remove\")";
719
720    for (i=0; i<256; i++) {
721        if (strchr(args->vparam[0].str, i)) tab[i] = 1;
722        else tab[i] = 0;
723    }
724
725    GBL_CHECK_FREE_PARAM(*args->coutput, args->cinput);
726    for (i=0; i<args->cinput; i++) {       // go through all orig streams
727        GBS_strstruct *strstruct = GBS_stropen(1000);
728        for (char *p = args->vinput[i].str; *p; p++) {
729            if (!tab[(unsigned int)*p]) {
730                GBS_chrcat(strstruct, *p);
731            }
732        }
733        PASS_2_OUT(args, GBS_strclose(strstruct));
734    }
735    return 0;
736}
737
738static GB_ERROR gbl_keep(GBL_command_arguments *args)
739{
740    int  i;
741    char tab[256];                              // if tab[char] != 0 then keep'char'
742
743
744    if (args->cparam!=1) return "keep syntax: keep(\"characters not to remove\")";
745
746    memset(tab, 0, 256); // keep none
747    {
748        unsigned char *keep = (unsigned char*)args->vparam[0].str;
749        for (i = 0; keep[i]; ++i) {
750            tab[keep[i]] = 1; // keep all members of argument
751        }
752    }
753
754    GBL_CHECK_FREE_PARAM(*args->coutput, args->cinput);
755    for (i=0; i<args->cinput; i++) {       // go through all orig streams
756        GBS_strstruct *strstruct = GBS_stropen(1000);
757        for (char *p = args->vinput[i].str; *p; p++) {
758            if (tab[(unsigned int)*p]) {
759                GBS_chrcat(strstruct, *p);
760            }
761        }
762        PASS_2_OUT(args, GBS_strclose(strstruct));
763    }
764    return 0;
765}
766
767static char *binop_compare(const char *arg1, const char *arg2, void *client_data) {
768    long case_sensitive = (long)client_data;
769    int result;
770
771    if (case_sensitive) result = strcmp(arg1, arg2);
772    else result                = gbl_stricmp(arg1, arg2);
773
774    return GBS_global_string_copy("%i", result<0 ? -1 : (result>0 ? 1 : 0));
775}
776static char *binop_equals(const char *arg1, const char *arg2, void *client_data) {
777    long case_sensitive = (long)client_data;
778    int result;
779
780    if (case_sensitive) result = strcmp(arg1, arg2);
781    else result                = gbl_stricmp(arg1, arg2);
782
783    return GBS_global_string_copy("%i", result == 0 ? 1 : 0);
784}
785static char *binop_contains(const char *arg1, const char *arg2, void *client_data) {
786    long         case_sensitive = (long)client_data;
787    const char *found          = 0;
788
789    if (case_sensitive) found = strstr(arg1, arg2);
790    else found                = gbl_stristr(arg1, arg2);
791
792    return GBS_global_string_copy("%ti", found == 0 ? 0 : (found-arg1)+1);
793}
794static char *binop_partof(const char *arg1, const char *arg2, void *client_data) {
795    return binop_contains(arg2, arg1, client_data);
796}
797
798static GB_ERROR gbl_compare  (GBL_command_arguments *args) { return gbl_apply_binary_operator(args, binop_compare, (void*)1); }
799static GB_ERROR gbl_icompare (GBL_command_arguments *args) { return gbl_apply_binary_operator(args, binop_compare, (void*)0); }
800static GB_ERROR gbl_equals   (GBL_command_arguments *args) { return gbl_apply_binary_operator(args, binop_equals,  (void*)1); }
801static GB_ERROR gbl_iequals  (GBL_command_arguments *args) { return gbl_apply_binary_operator(args, binop_equals,  (void*)0); }
802static GB_ERROR gbl_contains (GBL_command_arguments *args) { return gbl_apply_binary_operator(args, binop_contains, (void*)1); }
803static GB_ERROR gbl_icontains(GBL_command_arguments *args) { return gbl_apply_binary_operator(args, binop_contains, (void*)0); }
804static GB_ERROR gbl_partof   (GBL_command_arguments *args) { return gbl_apply_binary_operator(args, binop_partof,  (void*)1); }
805static GB_ERROR gbl_ipartof  (GBL_command_arguments *args) { return gbl_apply_binary_operator(args, binop_partof,  (void*)0); }
806
807static GB_ERROR gbl_translate(GBL_command_arguments *args)
808{
809    unsigned char tab[256];
810    int  i;
811    char replace_other = 0;
812
813    if (args->cparam<2 || args->cparam>3) return "translate syntax: translate(#old, #new [,#other])";
814
815    if (args->cparam == 3) {
816        const char *other = args->vparam[2].str;
817        if (other[0] == 0 || other[1] != 0) {
818            return "third parameter of translate has to be one character (i.e. \"-\")";
819        }
820        replace_other = other[0];
821    }
822
823    // build translation table :
824    {
825        const unsigned char *o = (const unsigned char *)args->vparam[0].str;
826        const unsigned char *n = (const unsigned char *)args->vparam[1].str;
827        char        used[256];
828
829        if (strlen((const char *)o) != strlen((const char *)n)) {
830            return "arguments 1 and 2 of translate should be strings with identical length";
831        }
832
833        for (i = 0; i<256; ++i) {
834            tab[i]  = replace_other ? replace_other : i; // replace unused or identity translation
835            used[i] = 0;
836        }
837
838        for (i = 0; o[i]; ++i) {
839            if (used[o[i]]) return GBS_global_string("character '%c' used twice in argument 1 of translate", o[i]);
840            used[o[i]] = 1;
841            tab[o[i]]  = n[i]; // real translation
842        }
843    }
844
845    GBL_CHECK_FREE_PARAM(*args->coutput, args->cinput);
846    for (i=0; i<args->cinput; i++) {       // go through all orig streams
847        GBS_strstruct *strstruct = GBS_stropen(1000);
848        for (char *p = args->vinput[i].str; *p; p++) {
849            GBS_chrcat(strstruct, tab[(unsigned char)*p]);
850        }
851        PASS_2_OUT(args, GBS_strclose(strstruct));
852    }
853    return 0;
854}
855
856
857static GB_ERROR gbl_echo(GBL_command_arguments *args)
858{
859    int i;
860    args->cinput = args->cinput;
861    args->vinput = args->vinput;
862    GBL_CHECK_FREE_PARAM(*args->coutput, args->cinput);
863    for (i=0; i<args->cparam; i++) {       // go through all in streams
864        char *p;
865        p = args->vparam[i].str;
866        COPY_2_OUT(args, p);
867    }
868    return 0;
869}
870
871static GB_ERROR gbl_dd(GBL_command_arguments *args)
872{
873    if (args->cparam!=0) return "syntax: dd (no parameters)";
874    return gbl_mid_streams(args->cinput, args->vinput, args->coutput, args->voutput, 0, 0, -1, 0); // copy all streams
875}
876
877static GB_ERROR gbl_string_convert(GBL_command_arguments *args)
878{
879    int mode  = -1;
880    int i;
881
882    if (strcmp(args->command, "lower")      == 0) mode = 0;
883    else if (strcmp(args->command, "upper") == 0) mode = 1;
884    else if (strcmp(args->command, "caps")  == 0) mode = 2;
885    else return GB_export_errorf("Unknown command '%s'", args->command);
886
887    if (args->cparam!=0) return GBS_global_string("syntax: %s (no parameters)", args->command);
888
889    GBL_CHECK_FREE_PARAM(*args->coutput, args->cinput);
890    for (i=0; i<args->cinput; i++) { // go through all in streams
891        char *p              = strdup(args->vinput[i].str);
892        char *pp;
893        int   last_was_alnum = 0;
894
895        for (pp = p; pp[0]; ++pp) {
896            switch (mode) {
897                case 0:  pp[0] = tolower(pp[0]); break;
898                case 1:  pp[0] = toupper(pp[0]); break;
899                case 2: // caps
900                    if (isalnum(pp[0])) {
901                        if (last_was_alnum) pp[0] = tolower(pp[0]);
902                        else pp[0]                = toupper(pp[0]);
903                        last_was_alnum            = 1;
904                    }
905                    else {
906                        last_was_alnum = 0;
907                    }
908                    break;
909                default: gb_assert(0); break;
910            }
911        }
912
913        PASS_2_OUT(args, p);
914    }
915
916    return 0;
917}
918
919static GB_ERROR gbl_head(GBL_command_arguments *args)
920{
921    int start;
922    if (args->cparam!=1) return "head syntax: head(#start)";
923    start = atoi(args->vparam[0].str);
924    return gbl_mid_streams(args->cinput, args->vinput, args->coutput, args->voutput, 0, 0, start, -start);
925}
926
927static GB_ERROR gbl_tail(GBL_command_arguments *args)
928{
929    int end;
930    if (args->cparam!=1) return "tail syntax: tail(#length_of_tail)";
931    end = atoi(args->vparam[0].str);
932    return gbl_mid_streams(args->cinput, args->vinput, args->coutput, args->voutput, -1, end, -1, 0);
933}
934
935static GB_ERROR gbl_mid0(GBL_command_arguments *args)
936{
937    int start;
938    int end;
939    if (args->cparam!=2) return "mid0 syntax: mid0(#start;#end)";
940    start = atoi(args->vparam[0].str);
941    end = atoi(args->vparam[1].str);
942    return gbl_mid_streams(args->cinput, args->vinput, args->coutput, args->voutput, start, -start, end, -end);
943}
944
945static GB_ERROR gbl_mid(GBL_command_arguments *args)
946{
947    int start;
948    int end;
949    if (args->cparam!=2) return "mid syntax: mid(#start;#end)";
950    start = atoi(args->vparam[0].str)-1;
951    end = atoi(args->vparam[1].str)-1;
952    return gbl_mid_streams(args->cinput, args->vinput, args->coutput, args->voutput, start, -start, end, -end);
953}
954
955static GB_ERROR gbl_tab(GBL_command_arguments *args)
956{
957    int i, j;
958    int tab;
959    if (args->cparam!=1) return "tab syntax: tab(#tab)";
960    tab = atoi(args->vparam[0].str);
961    GBL_CHECK_FREE_PARAM(*args->coutput, args->cinput);
962    for (i=0; i<args->cinput; i++) {       // go through all in streams
963        char *p;
964        int   len = strlen(args->vinput[i].str);
965
966        if (len >= tab) {
967            IN_2_OUT(args, i);
968        }
969        else {
970            p = (char *)GB_calloc(sizeof(char), tab+1);
971            strcpy(p, args->vinput[i].str);
972            for (j=len; j<tab; j++) p[j] = ' ';
973
974            PASS_2_OUT(args, p);
975        }
976    }
977    return 0;
978}
979
980static GB_ERROR gbl_pretab(GBL_command_arguments *args)
981{
982    int i, j;
983    int tab;
984    if (args->cparam!=1) return "pretab syntax: pretab(#tab)";
985    tab = atoi(args->vparam[0].str);
986    GBL_CHECK_FREE_PARAM(*args->coutput, args->cinput);
987    for (i=0; i<args->cinput; i++) {       // go through all in streams
988        char *p;
989        int   len = strlen(args->vinput[i].str);
990
991        if (len >= tab) {
992            IN_2_OUT(args, i);
993        }
994        else {
995            int spaces = tab-len;
996
997            p = (char *)GB_calloc(sizeof(char), tab+1);
998            for (j = 0; j<spaces; ++j) p[j] = ' ';
999            strcpy(p+j, args->vinput[i].str);
1000
1001            PASS_2_OUT(args, p);
1002        }
1003    }
1004    return 0;
1005}
1006
1007static GB_ERROR gbl_crop(GBL_command_arguments *args)
1008{
1009    int         i;
1010    const char *chars_to_crop;
1011
1012    if (args->cparam != 1) return "crop syntax: pretab(chars_to_crop)";
1013
1014    chars_to_crop = args->vparam[0].str;
1015    GBL_CHECK_FREE_PARAM(*args->coutput, args->cinput);
1016    for (i=0; i<args->cinput; i++) {       // go through all in streams
1017        const char *s = args->vinput[i].str;
1018        char       *p;
1019        int         len;
1020
1021        while (s[0] && strchr(chars_to_crop, s[0]) != 0) s++; // crop at beg of line
1022
1023        len = strlen(s);
1024        p   = (char*)GB_calloc(sizeof(char), len+1);
1025        memcpy(p, s, len);
1026
1027        {
1028            char *pe = p+len-1;
1029
1030            while (pe >= p && strchr(chars_to_crop, pe[0]) != 0) { // crop at end of line
1031                --pe;
1032            }
1033            gb_assert(pe >= (p-1));
1034            pe[1] = 0;
1035        }
1036
1037        PASS_2_OUT(args, p);
1038    }
1039    return 0;
1040}
1041
1042static GB_ERROR gbl_cut(GBL_command_arguments *args)
1043{
1044    int i;
1045
1046    GBL_CHECK_FREE_PARAM(*args->coutput, args->cparam);
1047    for (i=0; i<args->cparam; i++) {
1048        int j = atoi(args->vparam[i].str);
1049        if (j<1 || j>args->cinput) {
1050            return GBS_global_string("Illegal stream number '%i'", j);
1051        }
1052        --j;                    // user numbers streams from 1 to N
1053
1054        IN_2_OUT(args, j);
1055    }
1056    return 0;
1057}
1058static GB_ERROR gbl_drop(GBL_command_arguments *args)
1059{
1060    GB_ERROR error = 0;
1061
1062    GBL_CHECK_FREE_PARAM(*args->coutput, args->cinput-args->cparam);
1063
1064    int *dropped = (int*)malloc(args->cinput*sizeof(dropped));
1065    int  i;
1066    for (i=0; i<args->cinput; ++i) {
1067        dropped[i] = 0;
1068    }
1069
1070    for (i=0; i<args->cparam; ++i) {
1071        int j = atoi(args->vparam[i].str);
1072        if (j<1 || j>args->cinput) {
1073            error = GBS_global_string("Illegal stream number '%i'", j);
1074            break;
1075        }
1076        --j; // user numbers streams from 1 to N
1077        dropped[j] = 1;
1078    }
1079
1080    if (!error) {
1081        for (i=0; i<args->cinput; ++i) {
1082            if (dropped[i] == 0) {
1083                IN_2_OUT(args, i);
1084            }
1085        }
1086    }
1087    free(dropped);
1088
1089    return error;
1090}
1091
1092static GB_ERROR gbl_dropempty(GBL_command_arguments *args)
1093{
1094    int i;
1095    GBL_CHECK_FREE_PARAM(*args->coutput, args->cinput);
1096    if (args->cparam != 0) return "expect no parameters";
1097
1098    for (i=0; i<args->cinput; ++i) {
1099        if (args->vinput[i].str[0]) { // if non-empty
1100            IN_2_OUT(args, i);
1101        }
1102    }
1103    return 0;
1104}
1105
1106static GB_ERROR gbl_dropzero(GBL_command_arguments *args)
1107{
1108    int i;
1109    GBL_CHECK_FREE_PARAM(*args->coutput, args->cinput);
1110    if (args->cparam != 0) return "expect no parameters";
1111
1112    for (i=0; i<args->cinput; ++i) {
1113        if (atoi(args->vinput[i].str)) { // if non-zero
1114            IN_2_OUT(args, i);
1115        }
1116    }
1117    return 0;
1118}
1119
1120static GB_ERROR gbl_swap(GBL_command_arguments *args)
1121{
1122    int swap1;
1123    int swap2;
1124    int firstout = *args->coutput;
1125    int i;
1126
1127    GBL_CHECK_FREE_PARAM(*args->coutput, args->cinput);
1128    if (args->cinput<2) return "need at least two input streams";
1129
1130    if (args->cparam == 0) {
1131        swap1 = args->cinput-1;
1132        swap2 = args->cinput-2;
1133    }
1134    else if (args->cparam == 2) {
1135        int illegal = 0;
1136
1137        swap1 = atoi(args->vparam[0].str);
1138        swap2 = atoi(args->vparam[1].str);
1139
1140        if (swap1<1 || swap1>args->cinput) illegal      = swap1;
1141        else if (swap2<1 || swap2>args->cinput) illegal = swap2;
1142
1143        if (illegal) return GBS_global_string("illegal stream number '%i'", illegal);
1144
1145        swap1--;
1146        swap2--;
1147    }
1148    else {
1149        return "expected 0 or 2 parameters";
1150    }
1151
1152    for (i = 0; i<args->cinput; ++i) {
1153        IN_2_OUT(args, i);
1154    }
1155
1156    if (swap1 != swap2) {
1157        char *temp                           = (*args->voutput)[firstout+swap1].str;
1158        (*args->voutput)[firstout+swap1].str = (*args->voutput)[firstout+swap2].str;
1159        (*args->voutput)[firstout+swap2].str = temp;
1160    }
1161
1162    return 0;
1163}
1164
1165static GB_ERROR backfront_stream(GBL_command_arguments *args, int toback)
1166{
1167    int i;
1168    int stream_to_move;
1169    int firstout = *args->coutput;
1170
1171    GBL_CHECK_FREE_PARAM(*args->coutput, args->cinput);
1172    if (args->cinput<1) return "need at least one input stream";
1173    if (args->cparam != 1) return "expecting one parameter";
1174
1175    stream_to_move = atoi(args->vparam[0].str);
1176    if (stream_to_move<1 || stream_to_move>args->cinput) {
1177        return GBS_global_string("Illegal stream number '%i'", stream_to_move);
1178    }
1179    --stream_to_move;
1180
1181    for (i = 0; i<args->cinput; ++i) {
1182        IN_2_OUT(args, i);
1183    }
1184
1185    if (toback) {
1186        if (stream_to_move<(args->cinput-1)) { // not last
1187            GBL  new_last = (*args->voutput)[firstout+stream_to_move];
1188            for (i = stream_to_move+1; i<args->cinput; ++i) {
1189                (*args->voutput)[firstout+i-1] = (*args->voutput)[firstout+i];
1190            }
1191            (*args->voutput)[firstout+args->cinput-1] = new_last;
1192        }
1193    }
1194    else { // to front
1195        if (stream_to_move != 0) { // not first
1196            GBL new_first = (*args->voutput)[firstout+stream_to_move];
1197            for (i = stream_to_move-1;  i >= 0; --i) {
1198                (*args->voutput)[firstout+i+1] = (*args->voutput)[firstout+i];
1199            }
1200            (*args->voutput)[0] = new_first;
1201        }
1202    }
1203    return 0;
1204}
1205static GB_ERROR gbl_toback (GBL_command_arguments *args) { return backfront_stream(args, 1); }
1206static GB_ERROR gbl_tofront(GBL_command_arguments *args) { return backfront_stream(args, 0); }
1207
1208static GB_ERROR gbl_merge(GBL_command_arguments *args)
1209{
1210    const char *separator;
1211
1212    switch (args->cparam) {
1213        case 0: separator = 0; break;
1214        case 1: separator = args->vparam[0].str; break;
1215        default: return "expect 0 or 1 parameter";
1216    }
1217
1218    GBL_CHECK_FREE_PARAM(*args->coutput, 1);
1219
1220    if (args->cinput) {
1221        GBS_strstruct *str = GBS_stropen(1000);
1222        GBS_strcat(str, args->vinput[0].str);
1223
1224        for (int i = 1; i<args->cinput; ++i) {
1225            if (separator) GBS_strcat(str, separator);
1226            GBS_strcat(str, args->vinput[i].str);
1227        }
1228
1229        PASS_2_OUT(args, GBS_strclose(str));
1230    }
1231
1232    return 0;
1233}
1234
1235static GB_ERROR gbl_split(GBL_command_arguments *args) {
1236    const char *separator;
1237    int   split_mode = 0;       // 0 = remove separator, 1 = split before separator, 2 = split behind separator
1238
1239    switch (args->cparam) {
1240        case 0:                 // default behavior: split into lines and remove LF
1241            separator  = "\n";
1242            break;
1243        case 2:
1244            split_mode = atoi(args->vparam[1].str);
1245            if (split_mode<0 || split_mode>2) {
1246                return GBS_global_string("Illegal split mode '%i' (valid: 0..2)", split_mode);
1247            }
1248            // fall-through
1249        case 1:
1250            separator          = args->vparam[0].str;
1251            break;
1252        default: return "expect 0, 1 or 2 parameters";
1253    }
1254
1255    {
1256        size_t sepLen = strlen(separator);
1257        int    i;
1258
1259        for (i = 0; i<args->cinput; ++i) {
1260            char *in   = args->vinput[i].str;
1261            char *from = in; // search from here
1262
1263            while (in) {
1264                char *splitAt;
1265                GBL_CHECK_FREE_PARAM(*args->coutput, 1);
1266
1267                splitAt = strstr(from, separator);
1268                if (splitAt) {
1269                    size_t  len;
1270                    char   *copy;
1271
1272                    if (split_mode == 2) splitAt += sepLen; // split behind separator
1273
1274                    len  = splitAt-in;
1275                    copy = (char*)malloc(len+1);
1276
1277                    memcpy(copy, in, len);
1278                    copy[len] = 0;
1279
1280                    PASS_2_OUT(args, copy);
1281
1282                    in   = splitAt + (split_mode == 0 ? sepLen : 0);
1283                    from = in+(split_mode == 1 ? sepLen : 0);
1284                }
1285                else {          // last part
1286                    COPY_2_OUT(args, in);
1287                    in = 0;
1288                }
1289            }
1290        }
1291    }
1292
1293    return 0;
1294}
1295
1296// ----------------------------------
1297//      Extended string functions
1298
1299static GB_ERROR gbl_extract_words(GBL_command_arguments *args)
1300{
1301    int   i;
1302    float len;
1303    if (args->cparam != 2) return "extract_words needs two parameters:\nextract_words(\"Characters\",min_characters)";
1304    len = atof(args->vparam[1].str);
1305
1306    GBL_CHECK_FREE_PARAM(*args->coutput, args->cinput);
1307    for (i=0; i<args->cinput; i++) {       // go through all in streams
1308        char *res = GBS_extract_words(args->vinput[i].str, args->vparam[0].str, len, 1);
1309        gb_assert(res);
1310        PASS_2_OUT(args, res);
1311    }
1312    return 0;
1313}
1314
1315static GB_ERROR gbl_extract_sequence(GBL_command_arguments *args)
1316{
1317    int      i;
1318    float    len;
1319    GB_ERROR syntax_err = "extract_sequence needs two parameters:\nextract_sequence(\"Characters\",min_rel_characters [0.0-1.0])";
1320
1321    if (args->cparam != 2) return syntax_err;
1322    len = atof(args->vparam[1].str);
1323    if (len <0.0 || len > 1.0) return syntax_err;
1324
1325    GBL_CHECK_FREE_PARAM(*args->coutput, args->cinput);
1326    for (i=0; i<args->cinput; i++) {       // go through all in streams
1327        char *res = GBS_extract_words(args->vinput[i].str, args->vparam[0].str, len, 0);
1328        gb_assert(res);
1329        PASS_2_OUT(args, res);
1330    }
1331    return 0;
1332}
1333
1334static GB_ERROR gbl_check(GBL_command_arguments *args)
1335{
1336    int i;
1337
1338    GBL_BEGIN_PARAMS;
1339    GBL_PARAM_STRING(exclude, "exclude=", "", "Remove characters 'str' before calculating");
1340    GBL_PARAM_BIT   (upper,   "toupper",  0,  "Convert all characters to uppercase before calculating");
1341    GBL_TRACE_PARAMS(args->cparam, args->vparam);
1342    GBL_END_PARAMS;
1343
1344    GBL_CHECK_FREE_PARAM(*args->coutput, args->cinput);
1345    for (i=0; i<args->cinput; i++) {       // go through all in streams
1346        char buf[100];
1347        long id;
1348        id = GBS_checksum(args->vinput[i].str, upper, exclude);
1349        sprintf(buf, "%lX", id);
1350        COPY_2_OUT(args, buf);
1351    }
1352    return 0;
1353}
1354
1355static GB_ERROR gbl_gcgcheck(GBL_command_arguments *args)
1356{
1357    int i;
1358
1359    GBL_BEGIN_PARAMS;
1360    GBL_TRACE_PARAMS(args->cparam, args->vparam);
1361    GBL_END_PARAMS;
1362
1363    GBL_CHECK_FREE_PARAM(*args->coutput, args->cinput);
1364    for (i=0; i<args->cinput; i++) {       // go through all in streams
1365        char buf[100];
1366        long id;
1367        id = GBS_gcgchecksum(args->vinput[i].str);
1368        sprintf(buf, "%li", id);
1369        COPY_2_OUT(args, buf);
1370    }
1371    return 0;
1372}
1373
1374// ------------
1375//      SRT
1376
1377static GB_ERROR gbl_srt(GBL_command_arguments *args) {
1378    GB_ERROR error = NULL;
1379    int      i;
1380    int      j;
1381
1382    GBL_CHECK_FREE_PARAM(*args->coutput, args->cinput);
1383
1384    for (i=0; i<args->cinput && !error; i++) {      // go through all in streams
1385        const char *source    = args->vinput[i].str;
1386        char       *modsource = 0;
1387
1388        for (j=0; j<args->cparam && !error; j++) {
1389            char *hs = GBS_string_eval(modsource ? modsource : source,
1390                                       args->vparam[j].str,
1391                                       args->gb_ref);
1392
1393            if (!hs) error = GB_await_error();
1394            else freeset(modsource, hs);
1395        }
1396
1397        if (!error) {
1398            if (modsource) {
1399                PASS_2_OUT(args, modsource);
1400            }
1401            else {
1402                COPY_2_OUT(args, source);
1403            }
1404        }
1405    }
1406    return error;
1407}
1408
1409// -----------------------------
1410//      Calculator Functions
1411
1412
1413// numeric binary operators
1414
1415typedef int (*numeric_binary_operator)(int i1, int i2);
1416
1417static char *apply_numeric_binop(const char *arg1, const char *arg2, void *client_data) {
1418    int                     i1     = atoi(arg1);
1419    int                     i2     = atoi(arg2);
1420    numeric_binary_operator nbo    = (numeric_binary_operator)client_data;
1421    int                     result = nbo(i1, i2);
1422
1423    return GBS_global_string_copy("%i", result);
1424}
1425
1426static int binop_plus(int i1, int i2) { return i1+i2; }
1427static int binop_minus(int i1, int i2) { return i1-i2; }
1428static int binop_mult(int i1, int i2) { return i1*i2; }
1429static int binop_div(int i1, int i2) { return i2 ? i1/i2 : 0; }
1430static int binop_rest(int i1, int i2) { return i2 ? i1%i2 : 0; }
1431static int binop_per_cent(int i1, int i2) { return i2 ? (i1*100)/i2 : 0; }
1432
1433static GB_ERROR gbl_plus    (GBL_command_arguments *args) { return gbl_apply_binary_operator(args, apply_numeric_binop, (void*)binop_plus); }
1434static GB_ERROR gbl_minus   (GBL_command_arguments *args) { return gbl_apply_binary_operator(args, apply_numeric_binop, (void*)binop_minus); }
1435static GB_ERROR gbl_mult    (GBL_command_arguments *args) { return gbl_apply_binary_operator(args, apply_numeric_binop, (void*)binop_mult); }
1436static GB_ERROR gbl_div     (GBL_command_arguments *args) { return gbl_apply_binary_operator(args, apply_numeric_binop, (void*)binop_div); }
1437static GB_ERROR gbl_rest    (GBL_command_arguments *args) { return gbl_apply_binary_operator(args, apply_numeric_binop, (void*)binop_rest); }
1438static GB_ERROR gbl_per_cent(GBL_command_arguments *args) { return gbl_apply_binary_operator(args, apply_numeric_binop, (void*)binop_per_cent); }
1439
1440
1441static GB_ERROR gbl_select(GBL_command_arguments *args) {
1442    int       i;
1443    GB_ERROR  error   = 0;
1444
1445    GBL_CHECK_FREE_PARAM(*args->coutput, args->cinput);
1446
1447    for (i=0; i<args->cinput && !error; i++) { // go through all in streams
1448        int value = atoi(args->vinput[i].str);
1449        if (value<0 || value >= args->cparam) {
1450            error = GBS_global_string("Input stream value (%i) is out of bounds (0 to %i)", value, args->cparam-1);
1451        }
1452        else {
1453            GBDATA *gb_main    = (GBDATA*)GB_MAIN(args->gb_ref)->data;
1454            char   *result     = GB_command_interpreter(gb_main, "", args->vparam[value].str, args->gb_ref, args->default_tree_name);
1455            if (!result) error = GB_await_error();
1456            else    PASS_2_OUT(args, result);
1457        }
1458    }
1459    return error;
1460}
1461
1462static GB_ERROR gbl_readdb(GBL_command_arguments *args) {
1463    GBS_strstruct *strstr = GBS_stropen(1024);
1464
1465    for (int i=0; i<args->cparam; i++) {
1466        char *val = GBT_read_as_string(args->gb_ref, args->vparam[i].str);
1467        if (val) {
1468            GBS_strcat(strstr, val);
1469            free(val);
1470        }
1471    }
1472    PASS_2_OUT(args, GBS_strclose(strstr));
1473    return 0;
1474}
1475
1476
1477enum GBT_ITEM_TYPE {
1478    GBT_ITEM_UNKNOWN,
1479    GBT_ITEM_SPECIES,
1480    GBT_ITEM_GENE
1481};
1482
1483static GBT_ITEM_TYPE identify_gb_item(GBDATA *gb_item) {
1484    /* returns: GBT_ITEM_UNKNOWN    -> unknown database_item
1485     *          GBT_ITEM_SPECIES    -> /species_data/species
1486     *          GBT_ITEM_GENE       -> /species_data/species/gene_data/gene */
1487
1488    GBDATA     *gb_father;
1489    const char *key;
1490
1491    if (!gb_item) return GBT_ITEM_UNKNOWN;
1492    gb_father = GB_get_father(gb_item);
1493    if (!gb_father) return GBT_ITEM_UNKNOWN;
1494
1495    key = GB_KEY(gb_item);
1496
1497    if (strcmp(key, "species")                    == 0 &&
1498        strcmp(GB_KEY(gb_father), "species_data") == 0) {
1499        return GBT_ITEM_SPECIES;
1500    }
1501
1502    if (strcmp(key, "gene")                        == 0 &&
1503        strcmp(GB_KEY(gb_father), "gene_data")     == 0 &&
1504        identify_gb_item(GB_get_father(gb_father)) == GBT_ITEM_SPECIES) {
1505        return GBT_ITEM_GENE;
1506    }
1507
1508    return GBT_ITEM_UNKNOWN;
1509}
1510
1511// --------------------------------------------------------------------------------
1512// taxonomy caching
1513
1514#define GROUP_COUNT_CHARS 6                         // characters in taxonomy-key reserved for group-counter (hex number)
1515#define BITS_PER_HEXCHAR  4
1516#define MAX_GROUPS        (1 << (GROUP_COUNT_CHARS*BITS_PER_HEXCHAR)) // resulting number of groups
1517
1518struct cached_taxonomy {
1519    char    *tree_name;         // tree for which taxonomy is cached here
1520    int      groups;            // number of named groups in tree (at time of caching)
1521    GB_HASH *taxonomy; /* keys: "!species", ">XXXXgroup" and "<root>".
1522                        * Species and groups contain their first parent (i.e. '>XXXXgroup' or '<root>').
1523                        * Species not in hash are not members of tree.
1524                        * The 'XXXX' in groupname is simply a counter to avoid multiple groups with same name.
1525                        * The group-db-entries are stored in hash as pointers ('>>%p') and
1526                        * point to their own group entry ('>XXXXgroup')
1527                        *
1528                        * Note: the number of 'X's in 'XXXX' above is defined by GROUP_COUNT_CHARS!
1529                        */
1530};
1531
1532static void free_cached_taxonomy(cached_taxonomy *ct) {
1533    free(ct->tree_name);
1534    GBS_free_hash(ct->taxonomy);
1535    free(ct);
1536}
1537
1538static void build_taxonomy_rek(GBT_TREE *node, GB_HASH *tax_hash, const char *parent_group, int *group_counter) {
1539    if (node->is_leaf) {
1540        GBDATA *gb_species = node->gb_node;
1541        if (gb_species) { // not zombie
1542            GBS_write_hash(tax_hash, GBS_global_string("!%s", GBT_read_name(gb_species)), (long)strdup(parent_group));
1543        }
1544    }
1545    else {
1546        if (node->name) {       // named group
1547            char       *hash_entry;
1548            const char *hash_binary_entry;
1549            (*group_counter)++;
1550
1551            gb_assert((*group_counter)<MAX_GROUPS); // overflow - increase GROUP_COUNT_CHARS
1552
1553            hash_entry = GBS_global_string_copy(">%0*x%s", GROUP_COUNT_CHARS, *group_counter, node->name);
1554            GBS_write_hash(tax_hash, hash_entry, (long)strdup(parent_group));
1555
1556            hash_binary_entry = GBS_global_string(">>%p", node->gb_node);
1557            GBS_write_hash(tax_hash, hash_binary_entry, (long)strdup(hash_entry));
1558
1559            build_taxonomy_rek(node->leftson, tax_hash, hash_entry, group_counter);
1560            build_taxonomy_rek(node->rightson, tax_hash, hash_entry, group_counter);
1561            free(hash_entry);
1562        }
1563        else {
1564            build_taxonomy_rek(node->leftson, tax_hash, parent_group, group_counter);
1565            build_taxonomy_rek(node->rightson, tax_hash, parent_group, group_counter);
1566        }
1567    }
1568}
1569
1570static GB_HASH *cached_taxonomies = 0;
1571
1572static bool is_cached_taxonomy(const char */*key*/, long val, void *cl_ct) {
1573    cached_taxonomy *ct1 = (cached_taxonomy *)val;
1574    cached_taxonomy *ct2 = (cached_taxonomy *)cl_ct;
1575
1576    return ct1 == ct2;
1577}
1578
1579static const char *tree_of_cached_taxonomy(cached_taxonomy *ct) {
1580    /* search the hash to find the correct cached taxonomy.
1581     * searching for tree name does not work, because the tree possibly already was deleted
1582     */
1583    const char *tree = GBS_hash_next_element_that(cached_taxonomies, NULL, is_cached_taxonomy, ct);
1584#if defined(DEBUG)
1585    if (tree) printf("tree_of_cached_taxonomy: tree='%s' ct->tree_name='%s'\n", tree, ct->tree_name);
1586#endif // DEBUG
1587    return tree;
1588}
1589
1590static void flush_taxonomy_cb(GBDATA *gbd, int *cd_ct, GB_CB_TYPE /*cbt*/) {
1591    /* this cb is bound all tree db members below "/tree_data/tree_xxx" which
1592     * may have an effect on the displayed taxonomy
1593     * it invalidates cached taxonomies for that tree (when changed or deleted)
1594     */
1595
1596    cached_taxonomy *ct    = (cached_taxonomy *)cd_ct;
1597    const char      *found = 0;
1598    GB_ERROR         error = 0;
1599
1600    found = tree_of_cached_taxonomy(ct);
1601
1602    if (found) {
1603#if defined(DEBUG) && 0
1604        fprintf(stderr, "Deleting cached taxonomy ct=%p (tree='%s')\n", ct, found);
1605#endif // DEBUG
1606        GBS_write_hash(cached_taxonomies, found, 0); // delete cached taxonomy from hash
1607        free_cached_taxonomy(ct);
1608    }
1609#if defined(DEBUG) && 0
1610    else {
1611        fprintf(stderr, "No tree found for cached_taxonomies ct=%p (already deleted?)\n", ct);
1612    }
1613#endif // DEBUG
1614
1615    if (!GB_inside_callback(gbd, GB_CB_DELETE)) {
1616        GB_remove_all_callbacks_to(gbd, (GB_CB_TYPE)(GB_CB_CHANGED|GB_CB_DELETE), flush_taxonomy_cb);
1617    }
1618
1619    if (found && !error) {
1620        GBDATA *gb_main = GB_get_gb_main_during_cb();
1621        if (gb_main) {
1622            GBDATA *gb_tree_refresh = GB_search(gb_main, AWAR_TREE_REFRESH, GB_INT);
1623            if (!gb_tree_refresh) {
1624                error = GBS_global_string("%s (while trying to force refresh)", GB_await_error());
1625            }
1626            else {
1627                GB_touch(gb_tree_refresh); // Note : force tree update
1628            }
1629        }
1630    }
1631
1632    if (error) {
1633        fprintf(stderr, "Error in flush_taxonomy_cb: %s\n", error);
1634    }
1635}
1636
1637static void flush_taxonomy_if_new_group_cb(GBDATA *gb_tree, int *cd_ct, GB_CB_TYPE cbt) {
1638    // detects the creation of new groups and call flush_taxonomy_cb() manually
1639    cached_taxonomy *ct = (cached_taxonomy *)cd_ct;
1640    const char      *tree_name;
1641
1642#if defined(DEBUG)
1643    fprintf(stderr, "flush_taxonomy_if_new_group_cb() has been called (cbt=%i)\n", cbt);
1644#endif // DEBUG
1645
1646    tree_name = tree_of_cached_taxonomy(ct);
1647    if (tree_name) {
1648        int     groups = 0;
1649        GBDATA *gb_group_node;
1650
1651        for (gb_group_node = GB_entry(gb_tree, "node");
1652             gb_group_node;
1653             gb_group_node = GB_nextEntry(gb_group_node))
1654        {
1655            if (GB_entry(gb_group_node, "group_name")) {
1656                groups++; // count named groups only
1657            }
1658        }
1659
1660#if defined(DEBUG)
1661        fprintf(stderr, "cached_groups=%i  counted_groups=%i\n", ct->groups, groups);
1662#endif // DEBUG
1663        if (groups != ct->groups) {
1664#if defined(DEBUG)
1665            fprintf(stderr, "Number of groups changed -> invoking flush_taxonomy_cb() manually\n");
1666#endif // DEBUG
1667            flush_taxonomy_cb(gb_tree, cd_ct, cbt);
1668        }
1669    }
1670#if defined(DEBUG)
1671    else {
1672        fprintf(stderr, "cached taxonomy no longer valid.\n");
1673    }
1674#endif // DEBUG
1675}
1676
1677static cached_taxonomy *get_cached_taxonomy(GBDATA *gb_main, const char *tree_name, GB_ERROR *error) {
1678    long cached;
1679    *error = 0;
1680    if (!cached_taxonomies) {
1681        cached_taxonomies = GBS_create_hash(20, GB_IGNORE_CASE);
1682    }
1683    cached = GBS_read_hash(cached_taxonomies, tree_name);
1684    if (!cached) {
1685        GBT_TREE *tree    = GBT_read_tree(gb_main, tree_name, sizeof(*tree));
1686        if (!tree) *error = GB_await_error();
1687        else     *error   = GBT_link_tree(tree, gb_main, false, 0, 0);
1688
1689        if (!*error) {
1690            GBDATA *gb_tree = GBT_get_tree(gb_main, tree_name);
1691            if (!gb_tree) {
1692                *error = GBS_global_string("Can't find tree '%s'", tree_name);
1693            }
1694            else {
1695                cached_taxonomy *ct            = (cached_taxonomy*)malloc(sizeof(*ct));
1696                long             nodes         = GBT_count_leafs(tree);
1697                int              group_counter = 0;
1698
1699                ct->tree_name = strdup(tree_name);
1700                ct->taxonomy  = GBS_create_dynaval_hash(int(nodes), GB_IGNORE_CASE, GBS_dynaval_free);
1701                ct->groups    = 0; // counted below
1702
1703                build_taxonomy_rek(tree, ct->taxonomy, "<root>", &group_counter);
1704                cached = (long)ct;
1705                GBS_write_hash(cached_taxonomies, tree_name, (long)ct);
1706
1707                GB_remove_all_callbacks_to(gb_tree, GB_CB_SON_CREATED, flush_taxonomy_if_new_group_cb);
1708                GB_add_callback(gb_tree, GB_CB_SON_CREATED, flush_taxonomy_if_new_group_cb, (int*)ct);
1709
1710                {
1711                    GBDATA *gb_tree_entry = GB_entry(gb_tree, "tree");
1712                    GBDATA *gb_group_node;
1713
1714                    if (gb_tree_entry) {
1715                        GB_remove_all_callbacks_to(gb_tree_entry, (GB_CB_TYPE)(GB_CB_CHANGED|GB_CB_DELETE), flush_taxonomy_cb);
1716                        GB_add_callback(gb_tree_entry, (GB_CB_TYPE)(GB_CB_CHANGED|GB_CB_DELETE), flush_taxonomy_cb, (int*)ct);
1717                    }
1718
1719                    // add callbacks for all node/group_name subentries
1720                    for (gb_group_node = GB_entry(gb_tree, "node");
1721                         gb_group_node;
1722                         gb_group_node = GB_nextEntry(gb_group_node))
1723                    {
1724                        GBDATA *gb_group_name = GB_entry(gb_group_node, "group_name");
1725                        if (gb_group_name) { // group with id = 0 has no name
1726                            GB_remove_all_callbacks_to(gb_group_name, (GB_CB_TYPE)(GB_CB_CHANGED|GB_CB_DELETE), flush_taxonomy_cb);
1727                            GB_add_callback(gb_group_name, (GB_CB_TYPE)(GB_CB_CHANGED|GB_CB_DELETE), flush_taxonomy_cb, (int*)ct);
1728                            ct->groups++;
1729                        }
1730                    }
1731                }
1732#if defined(DEBUG)
1733                fprintf(stderr, "Created taxonomy hash for '%s' (ct=%p)\n", tree_name, ct);
1734#endif // DEBUG
1735            }
1736        }
1737
1738        if (tree) GBT_delete_tree(tree);
1739    }
1740
1741    if (!*error) {
1742        cached_taxonomy *ct = (cached_taxonomy*)cached;
1743        gb_assert(ct);
1744        return ct;
1745    }
1746
1747    return 0;
1748}
1749
1750static char *get_taxonomy_string(GB_HASH *tax_hash, const char *group_key, int depth, GB_ERROR *error) {
1751    long  found;
1752    char *result = 0;
1753
1754    gb_assert(depth>0);
1755    gb_assert(!(group_key[0] == '>' && group_key[1] == '>')); // internal group-pointers not allowed here!
1756
1757    found = GBS_read_hash(tax_hash, group_key);
1758    if (found) {
1759        const char *parent_group_key            = (const char *)found;
1760        if (strcmp(parent_group_key, "<root>") == 0) { // root reached
1761            result = strdup(group_key+(GROUP_COUNT_CHARS+1)); // return own group name
1762        }
1763        else {
1764            if (depth>1) {
1765                char *parent_name = get_taxonomy_string(tax_hash, parent_group_key, depth-1, error);
1766                if (parent_name) {
1767                    result = GBS_global_string_copy("%s/%s", parent_name, group_key+(GROUP_COUNT_CHARS+1));
1768                    free(parent_name);
1769                }
1770                else {
1771                    *error = GBS_global_string("In get_taxonomy_string(%s): %s", group_key, *error);
1772                    result = 0;
1773                }
1774            }
1775            else {
1776                result = strdup(group_key+(GROUP_COUNT_CHARS+1)); // return own group name
1777            }
1778        }
1779    }
1780    else {
1781        *error = GBS_global_string("Not in tax_hash: '%s'", group_key);
1782    }
1783    return result;
1784}
1785
1786static const char *get_taxonomy(GBDATA *gb_species_or_group, const char *tree_name, int is_current_tree, int depth, GB_ERROR *error) {
1787    GBDATA          *gb_main = GB_get_root(gb_species_or_group);
1788    cached_taxonomy *tax     = get_cached_taxonomy(gb_main, tree_name, error);
1789    const char      *result  = 0;
1790
1791    if (tax) {
1792        GBDATA *gb_name       = GB_entry(gb_species_or_group, "name");
1793        GBDATA *gb_group_name = GB_entry(gb_species_or_group, "group_name");
1794
1795        if (gb_name && !gb_group_name) { // it's a species
1796            char *name = GB_read_string(gb_name);
1797            if (name) {
1798                GB_HASH *tax_hash = tax->taxonomy;
1799                long     found    = GBS_read_hash(tax_hash, GBS_global_string("!%s", name));
1800
1801                if (found) {
1802                    const char *parent_group = (const char *)found;
1803
1804                    if (strcmp(parent_group, "<root>") == 0) {
1805                        result = ""; // not member of any group
1806                    }
1807                    else {
1808                        static char *parent = 0;
1809
1810                        freeset(parent, get_taxonomy_string(tax_hash, parent_group, depth, error));
1811                        result = parent;
1812                    }
1813                }
1814                else {
1815                    result = GBS_global_string("Species '%s' not in '%s'", name, tree_name);
1816                }
1817                free(name);
1818            }
1819            else {
1820                *error = GBS_global_string("Species without 'name' entry!");
1821            }
1822        }
1823        else if (gb_group_name && !gb_name) { // it's a group
1824            char *group_name = GB_read_string(gb_group_name);
1825            if (group_name) {
1826                if (is_current_tree) {
1827                    GB_HASH *tax_hash = tax->taxonomy;
1828                    long     found    = GBS_read_hash(tax_hash, GBS_global_string(">>%p", gb_species_or_group));
1829
1830                    if (found) {
1831                        static char *full_group = 0;
1832                        const char  *group_id   = (const char *)found;
1833
1834                        freeset(full_group, get_taxonomy_string(tax_hash, group_id, depth, error));
1835                        result = full_group;
1836                    }
1837                    else {
1838                        result = GBS_global_string("Group '%s' not in '%s'", group_name, tree_name);
1839                    }
1840                }
1841                else {
1842                    *error = "It's not possible to specify the tree name in taxonomy() for groups";
1843                }
1844                free(group_name);
1845            }
1846            else {
1847                *error = "Group without 'group_name' entry";
1848            }
1849        }
1850        else if (gb_group_name) {
1851            *error = "Container has 'name' and 'group_name' entry - can't detect container type";
1852        }
1853        else {
1854            *error = "Container has neither 'name' nor 'group_name' entry - can't detect container type";
1855        }
1856    }
1857
1858    return result;
1859}
1860
1861static GB_ERROR gbl_taxonomy(GBL_command_arguments *args)
1862{
1863    char     *tree_name = 0;
1864    GB_ERROR  error     = 0;
1865
1866
1867    if (args->cparam<1 || args->cparam>2) {
1868        error = "\"taxonomy\" syntax: \"taxonomy\"([tree_name,] count)";
1869    }
1870    else {
1871        int   is_current_tree = 0;
1872        int   count           = -1;
1873        char *result          = 0;
1874
1875        if (args->cparam == 1) {   // only 'count'
1876            if (!args->default_tree_name) {
1877                result = strdup("No default tree");
1878            }
1879            else {
1880                tree_name = strdup(args->default_tree_name);
1881                count = atoi(args->vparam[0].str);
1882                is_current_tree = 1;
1883            }
1884        }
1885        else { // 'tree_name', 'count'
1886            tree_name = strdup(args->vparam[0].str);
1887            count     = atoi(args->vparam[1].str);
1888        }
1889
1890        if (!result) {
1891            if (count<1) {
1892                error = GBS_global_string("Illegal count '%i' (allowed 1..n)", count);
1893            }
1894            if (!error) {
1895                const char *taxonomy_string = get_taxonomy(args->gb_ref, tree_name, is_current_tree, count, &error);
1896                if (taxonomy_string) result = strdup(taxonomy_string);
1897            }
1898        }
1899
1900        gb_assert(result || error);
1901        if (result) PASS_2_OUT(args, result);
1902    }
1903    if (tree_name) free(tree_name);
1904    return error;
1905}
1906
1907static GB_ERROR gbl_sequence(GBL_command_arguments *args) {
1908    GB_ERROR  error = 0;
1909
1910    if (args->cparam!=0) error      = "\"sequence\" syntax: \"sequence\" (no parameters)";
1911    else if (args->cinput==0) error = "No input stream";
1912    else {
1913        GBL_CHECK_FREE_PARAM(*args->coutput, 1);
1914
1915        switch (identify_gb_item(args->gb_ref)) {
1916            case GBT_ITEM_UNKNOWN: {
1917                error = "'sequence' used for unknown item";
1918                break;
1919            }
1920            case GBT_ITEM_SPECIES: {
1921                char *use = GBT_get_default_alignment(GB_get_root(args->gb_ref));
1922
1923                if (!use) error = GB_await_error();
1924                else {
1925                    GBDATA *gb_seq = GBT_read_sequence(args->gb_ref, use);
1926
1927                    if (gb_seq) PASS_2_OUT(args, GB_read_string(gb_seq));
1928                    else        COPY_2_OUT(args, ""); // if current alignment does not exist -> return empty string
1929
1930                    free(use);
1931                }
1932                break;
1933            }
1934            case GBT_ITEM_GENE: {
1935                char *seq = GBT_read_gene_sequence(args->gb_ref, true, 0);
1936
1937                if (!seq) error = GB_await_error();
1938                else PASS_2_OUT(args, seq);
1939
1940                break;
1941            }
1942        }
1943    }
1944    return error;
1945}
1946
1947static GB_ERROR gbl_export_sequence(GBL_command_arguments *args) {
1948    GB_ERROR error = 0;
1949
1950    if (args->cparam!=0) {
1951        error = "\"sequence\" syntax: \"export_sequence\" (no parameters)";
1952    }
1953    else if (args->cinput==0) {
1954        error = "No input stream";
1955    }
1956    else {
1957        GBL_CHECK_FREE_PARAM(*args->coutput, 1);
1958
1959        switch (identify_gb_item(args->gb_ref)) {
1960            case GBT_ITEM_UNKNOWN: {
1961                error = "'export_sequence' used for unknown item";
1962                break;
1963            }
1964            case GBT_ITEM_SPECIES: {
1965                if (get_export_sequence == 0) {
1966                    error = "No export-sequence-hook defined (can't use 'export_sequence' here)";
1967                }
1968                else {
1969                    size_t      len;
1970                    const char *seq = get_export_sequence(args->gb_ref, &len, &error);
1971
1972                    gb_assert(error || seq);
1973
1974                    if (seq) PASS_2_OUT(args, GB_strduplen(seq, len));
1975                }
1976                break;
1977            }
1978            case GBT_ITEM_GENE: {
1979                error = "'export_sequence' cannot be used for gene";
1980                break;
1981            }
1982        }
1983    }
1984    return error;
1985}
1986
1987static GB_ERROR gbl_sequence_type(GBL_command_arguments *args)
1988{
1989    GB_ERROR error = 0;
1990
1991    if (args->cparam) error = "\"sequence_type\" syntax: \"sequence\" (no parameters)";
1992    else {
1993        GBL_CHECK_FREE_PARAM(*args->coutput, 1);
1994        if (!args->cinput) error = "No input stream";
1995        else {
1996            char *use = GBT_get_default_alignment(GB_get_root(args->gb_ref));
1997            PASS_2_OUT(args, GBT_get_alignment_type_string(GB_get_root(args->gb_ref), use));
1998            free(use);
1999        }
2000    }
2001
2002    return error;
2003}
2004
2005static GB_ERROR gbl_format_sequence(GBL_command_arguments *args)
2006{
2007    GB_ERROR error = 0;
2008    int      ic;
2009
2010    GBL_BEGIN_PARAMS;
2011    GBL_PARAM_UINT  (firsttab, "firsttab=", 10,   "Indent first line");
2012    GBL_PARAM_UINT  (tab,      "tab=",      10,   "Indent not first line");
2013    GBL_PARAM_BIT   (numleft,  "numleft",   0,    "Numbers left of sequence");
2014    GBL_PARAM_UINT  (gap,      "gap=",      10,   "Insert ' ' every n sequence characters");
2015    GBL_PARAM_UINT  (width,    "width=",    50,   "Sequence width (bases only)");
2016    GBL_PARAM_STRING(nl,       "nl=",       " ",  "Break line at characters 'str' if wrapping needed");
2017    GBL_PARAM_STRING(forcenl,  "forcenl=",  "\n", "Always break line at characters 'str'");
2018    GBL_TRACE_PARAMS(args->cparam, args->vparam);
2019    GBL_END_PARAMS;
2020
2021    for (ic = 0; ic<args->cinput; ++ic) {
2022        GBL_CHECK_FREE_PARAM(*args->coutput, 1);
2023
2024        {
2025            const char *src           = args->vinput[ic].str;
2026            size_t      data_size     = strlen(src);
2027            size_t      needed_size;
2028            int         simple_format = (strcmp(args->command, "format") == 0);
2029
2030            {
2031                size_t lines;
2032                size_t line_size;
2033
2034                if (simple_format) {
2035                    lines     = data_size/2 + 1; // worst case
2036                    line_size = tab + width + 1;
2037                }
2038                else {
2039                    size_t gapsPerLine = (width-1)/gap;
2040                    lines              = data_size/width+1;
2041                    line_size          = tab + width + gapsPerLine + 1;
2042                }
2043
2044                needed_size = lines*line_size + firsttab + 1 + 10;
2045            }
2046
2047            char *result = (char*)malloc(needed_size);
2048            if (!result) {
2049                error = GBS_global_string("Out of memory (tried to alloc %zu bytes)", needed_size);
2050            }
2051            else {
2052                char   *dst       = result;
2053                size_t  rest_data = data_size;
2054                int     i;
2055
2056                if (simple_format) {
2057                    /* format string w/o gaps or numleft
2058                     * does word-wrapping at chars in nl
2059                     */
2060
2061                    // build wrap table
2062                    unsigned char isWrapChar[256];
2063                    memset(isWrapChar, 0, sizeof(isWrapChar));
2064                    for (i = 0; nl[i]; ++i) isWrapChar[(unsigned char)nl[i]] = 1;
2065                    for (i = 0; forcenl[i]; ++i) isWrapChar[(unsigned char)forcenl[i]] = 2;
2066
2067                    if (firsttab>0) {
2068                        memset(dst, ' ', firsttab);
2069                        dst += firsttab;
2070                    }
2071
2072                    while (rest_data>width) {
2073                        int take;
2074                        int move;
2075                        int took;
2076
2077                        for (take = width; take > 0; --take) {
2078                            if (isWrapChar[(unsigned char)src[take]]) break;
2079                        }
2080                        if (take <= 0) { // no wrap character found -> hard wrap at width
2081                            take  = move = width;
2082                        }
2083                        else { // soft wrap at last found wrap character
2084                            move = take+1;
2085                        }
2086
2087                        for (took = 0; took<take; took++) {
2088                            char c = src[took];
2089                            if (isWrapChar[(unsigned char)c] == 2) { // forced newline
2090                                take = took;
2091                                move = take+1;
2092                                break;
2093                            }
2094                            dst[took] = c;
2095                        }
2096
2097                        dst       += take;
2098                        src       += move;
2099                        rest_data -= move;
2100
2101                        if (rest_data>0) {
2102                            *dst++ = '\n';
2103                            if (tab>0) {
2104                                memset(dst, ' ', tab);
2105                                dst += tab;
2106                            }
2107                        }
2108                    }
2109
2110                    if (rest_data>0) {
2111                        size_t j, k;
2112                        for (j = 0, k = 0; j<rest_data; ++j) {
2113                            char c = src[j];
2114
2115                            if (isWrapChar[(unsigned char)c] == 2) {
2116                                dst[k++] = '\n';
2117                                if (tab>0) {
2118                                    memset(dst+k, ' ', tab);
2119                                    k += tab;
2120                                }
2121                            }
2122                            else {
2123                                dst[k++] = c;
2124                            }
2125                        }
2126                        src       += j;
2127                        dst       += k;
2128                        rest_data  = 0;
2129                    }
2130                }
2131                else {
2132                    // "format_sequence" with gaps and numleft
2133                    char       *format    = 0;
2134                    const char *src_start = src;
2135
2136                    if (numleft) {
2137                        /* Warning: Be very careful, when you change format strings here!
2138                         * currently all format strings result in '%u' or '%-##u' (where # are digits)
2139                         */
2140                        if (firsttab>0) {
2141                            char *firstFormat = GBS_global_string_copy("%%-%iu ", firsttab-1);
2142                            dst += sprintf(dst, firstFormat, (unsigned)1);
2143                            free(firstFormat);
2144                        }
2145                        else {
2146                            dst += sprintf(dst, "%u ", (unsigned)1);
2147                        }
2148                        format = tab>0 ? GBS_global_string_copy("%%-%iu ", tab-1) : strdup("%u ");
2149                    }
2150                    else if (firsttab>0) {
2151                        memset(dst, ' ', firsttab);
2152                        dst += firsttab;
2153                    }
2154
2155                    while (rest_data>0) {
2156                        size_t take = rest_data>width ? width : rest_data;
2157
2158                        rest_data -= take;
2159
2160                        while (take>gap) {
2161                            memcpy(dst, src, gap);
2162                            dst  += gap;
2163                            src  += gap;
2164                            *dst++ = ' ';
2165                            take -= gap;
2166                        }
2167
2168                        memcpy(dst, src, take);
2169                        dst += take;
2170                        src += take;
2171
2172                        if (rest_data>0) {
2173                            *dst++ = '\n';
2174                            if (numleft) {
2175                                unsigned int num  = (src-src_start)+1; // this goes to the '%u' (see comment above)
2176                                dst              += sprintf(dst, format, num);
2177                            }
2178                            else if (tab>0) {
2179                                memset(dst, ' ', tab);
2180                                dst += tab;
2181                            }
2182                        }
2183                    }
2184
2185                    free(format);
2186                }
2187
2188                *dst++ = 0;         // close str
2189
2190#if defined(DEBUG)
2191                { // check for array overflow
2192                    size_t used_size = dst-result;
2193                    gb_assert(used_size <= needed_size);
2194
2195                    char *new_result = (char*)realloc(result, used_size);
2196                    if (!new_result) {
2197                        error = "Out of memory";
2198                    }
2199                    else {
2200                        result = new_result;
2201                    }
2202                }
2203#endif // DEBUG
2204            }
2205
2206            if (!error) PASS_2_OUT(args, result);
2207            else free(result);
2208        }
2209    }
2210    return error;
2211}
2212
2213static char *gbl_read_seq_sai_or_species(const char *species, const char *sai, const char *ali, size_t *seqLen) {
2214    /* Reads the alignment 'ali'  of 'species' or 'sai'.
2215     * If 'ali' is NULL, use default alignment.
2216     * Returns NULL in case of error (which is exported then)
2217     */
2218
2219    char     *seq   = NULL;
2220    GB_ERROR  error = 0;
2221
2222    int sources = !!species + !!sai;
2223    if (sources != 1) {
2224        error = "Either parameters 'species' or 'SAI' must be specified";
2225    }
2226    else {
2227        GBDATA     *gb_main = gb_local->gbl.gb_main;
2228        GBDATA     *gb_item = 0;
2229        const char *what    = 0;
2230        const char *name    = 0;
2231
2232        if (species) {
2233            gb_item = GBT_find_species(gb_main, species);
2234            what    = "species";
2235            name    = species;
2236        }
2237        else {
2238            gb_item = GBT_find_SAI(gb_main, sai);
2239            what    = "SAI";
2240            name    = sai;
2241        }
2242
2243        if (!gb_item) error = GBS_global_string("Can't find %s '%s'", what, name);
2244        else {
2245            char *freeMe = 0;
2246
2247            if (!ali) {
2248                ali = freeMe = GBT_get_default_alignment(gb_main);
2249                if (!ali) error = "can't detect default alignment";
2250            }
2251
2252            if (ali) {
2253                GBDATA *gb_ali = GB_entry(gb_item, ali);
2254
2255                if (gb_ali) {
2256                    GBDATA *gb_seq;
2257
2258                    for (gb_seq = GB_child(gb_ali); gb_seq; gb_seq = GB_nextChild(gb_seq)) {
2259                        long type = GB_read_type(gb_seq);
2260                        if (type == GB_BITS) {
2261                            seq     = GB_read_bits(gb_seq, '-', '+');
2262                            if (seqLen) *seqLen = GB_read_bits_count(gb_seq);
2263                            break;
2264                        }
2265                        if (type == GB_STRING) {
2266                            seq     = GB_read_string(gb_seq);
2267                            if (seqLen) *seqLen = GB_read_string_count(gb_seq);
2268                            break;
2269                        }
2270                    }
2271                }
2272
2273                if (!seq) error = GBS_global_string("%s '%s' has no (usable) data in alignment '%s'", what, name, ali);
2274            }
2275            free(freeMe);
2276        }
2277    }
2278
2279    if (error) {
2280        gb_assert(!seq);
2281        GB_export_error(error);
2282    }
2283
2284    return seq;
2285}
2286
2287struct common_filter_params {
2288    const char *align;
2289    const char *sai;
2290    const char *species;
2291    int         first;
2292    int         pairwise;
2293};
2294
2295#define GBL_COMMON_FILTER_PARAMS                                        \
2296    common_filter_params common_param;                                  \
2297    GBL_STRUCT_PARAM_STRING(common_param, align,    "align=",    0,   "alignment to use (defaults to default alignment)"); \
2298    GBL_STRUCT_PARAM_STRING(common_param, sai,      "SAI=",      0,   "Use default sequence of given SAI as a filter"); \
2299    GBL_STRUCT_PARAM_STRING(common_param, species,  "species=",  0,   "Use default sequence of given species as a filter"); \
2300    GBL_STRUCT_PARAM_BIT   (common_param, first,    "first=",    0,   "Use 1st stream as filter for other streams"); \
2301    GBL_STRUCT_PARAM_BIT   (common_param, pairwise, "pairwise=", 0,   "Use 1st stream as filter for 2nd, 3rd for 4th, ...")
2302
2303typedef char* (*filter_fun)(const char *seq, const char *filter, size_t flen, void *param);
2304/* Note:
2305 * filter_fun has to return a heap copy of the filter-result.
2306 * if 'flen' != 0, it contains the length of 'filter'
2307 * 'param' may be any client data
2308 */
2309
2310static GB_ERROR apply_filters(GBL_command_arguments *args, common_filter_params *common, filter_fun filter_one, void *param) {
2311    GB_ERROR error = 0;
2312
2313    if (args->cinput==0) error = "No input stream";
2314    else {
2315        int methodCount = !!common->sai + !!common->species + !!common->pairwise + !!common->first;
2316
2317        if (methodCount != 1) error = "Need exactly one of the parameters 'SAI', 'species', 'pairwise' or 'first'";
2318        else {
2319            if (common->pairwise) {
2320                if (args->cinput % 2) error = "Using 'pairwise' requires an even number of input streams";
2321                else {
2322                    int i;
2323                    for (i = 1; i<args->cinput; i += 2) {
2324                        PASS_2_OUT(args, filter_one(args->vinput[i].str, args->vinput[i-1].str, 0, param));
2325                    }
2326                }
2327            }
2328            else {
2329                int     i      = 0;
2330                char   *filter = 0;
2331                size_t  flen   = 0;
2332
2333                if (common->first) {
2334                    if (args->cinput<2) error = "Using 'first' needs at least 2 input streams";
2335                    else {
2336                        const char *in = args->vinput[i++].str;
2337                        gb_assert(in);
2338
2339                        flen   = strlen(in);
2340                        filter = GB_strduplen(in, flen);
2341                    }
2342                }
2343                else {
2344                    filter = gbl_read_seq_sai_or_species(common->species, common->sai, common->align, &flen);
2345                    if (!filter) error = GB_await_error();
2346                }
2347
2348                gb_assert(filter || error);
2349                if (filter) {
2350                    for (; i<args->cinput; ++i) {
2351                        PASS_2_OUT(args, filter_one(args->vinput[i].str, filter, flen, param));
2352                    }
2353                }
2354                free(filter);
2355            }
2356        }
2357    }
2358    return error;
2359}
2360
2361// -------------------------
2362//      calculate diff
2363
2364struct diff_params {
2365    char equalC;
2366    char diffC;
2367};
2368static char *calc_diff(const char *seq, const char *filter, size_t /*flen*/, void *paramP) {
2369    // filters 'seq' through 'filter'
2370    // - replace all equal     positions by 'equal_char' (if != 0)
2371    // - replace all differing positions by 'diff_char'  (if != 0)
2372
2373    diff_params *param      = (diff_params*)paramP;
2374    char         equal_char = param->equalC;
2375    char         diff_char  = param->diffC;
2376
2377    char *result = strdup(seq);
2378    int   p;
2379
2380    for (p = 0; result[p] && filter[p]; ++p) {
2381        if (result[p] == filter[p]) {
2382            if (equal_char) result[p] = equal_char;
2383        }
2384        else {
2385            if (diff_char) result[p] = diff_char;
2386        }
2387    }
2388
2389    // if 'seq' is longer than 'filter' and diff_char is given
2390    // -> fill rest of 'result' with 'diff_char'
2391    if (diff_char) {
2392        for (; result[p]; ++p) {
2393            result[p] = diff_char;
2394        }
2395    }
2396
2397    return result;
2398}
2399static GB_ERROR gbl_diff(GBL_command_arguments *args) {
2400    GBL_BEGIN_PARAMS;
2401    GBL_COMMON_FILTER_PARAMS;
2402
2403    diff_params param;
2404    GBL_STRUCT_PARAM_CHAR(param, equalC,   "equal=",    '.', "symbol for equal characters");
2405    GBL_STRUCT_PARAM_CHAR(param, diffC,    "differ=",   0,   "symbol for diff characters (default: use char from input stream)");
2406
2407    GBL_TRACE_PARAMS(args->cparam, args->vparam);
2408    GBL_END_PARAMS;
2409
2410    GBL_CHECK_FREE_PARAM(*args->coutput, args->cinput);
2411
2412    return apply_filters(args, &common_param, calc_diff, &param);
2413}
2414
2415// -------------------------
2416//      standard filter
2417
2418enum filter_function { FP_FILTER, FP_MODIFY };
2419
2420struct filter_params { // used by gbl_filter and gbl_change_gc
2421    filter_function function;
2422
2423    const char *include;
2424    const char *exclude;
2425
2426    // FP_MODIFY only:
2427    int         change_pc;
2428    const char *change_to;
2429};
2430
2431static char *filter_seq(const char *seq, const char *filter, size_t flen, void *paramP) {
2432    filter_params *param = (filter_params*)paramP;
2433
2434    size_t slen     = strlen(seq);
2435    if (!flen) flen = strlen(filter);
2436    size_t mlen     = slen<flen ? slen : flen;
2437
2438    GBS_strstruct *out = GBS_stropen(mlen);
2439
2440    const char *charset;
2441    int         include;
2442
2443    if (param->include) {
2444        charset = param->include;
2445        include = 1;
2446    }
2447    else {
2448        gb_assert(param->exclude);
2449        charset = param->exclude;
2450        include = 0;
2451    }
2452
2453    size_t pos  = 0;
2454    size_t rest = slen;
2455    size_t ctl  = 0;
2456    if (param->function == FP_MODIFY) ctl  = strlen(param->change_to);
2457
2458    int inset = 1; // 1 -> check chars in charset, 0 -> check chars NOT in charset
2459    while (rest) {
2460        size_t count;
2461        if (pos >= flen) {      // behind filter
2462            // trigger last loop
2463            count = rest;
2464            inset = 0; // if 'include' -> 'applies' will get false, otherwise true
2465                       // (meaning is: behind filter nothing can match 'include' or 'exclude')
2466        }
2467        else {
2468            count = (inset ? strspn : strcspn)(filter+pos, charset); // count how many chars are 'inset'
2469        }
2470        if (count) {
2471            int applies = !!include == !!inset; // true -> 'filter' matches 'include' or doesn't match 'exclude'
2472            if (count>rest) count = rest;
2473
2474            switch (param->function) {
2475                case FP_FILTER:
2476                    if (applies) GBS_strncat(out, seq+pos, count);
2477                    break;
2478
2479                case FP_MODIFY:
2480                    if (applies) { // then modify
2481                        size_t i;
2482                        for (i = 0; i<count; i++) {
2483                            char c = seq[pos+i];
2484                            if (isalpha(c) && GB_random(100)<param->change_pc) c = param->change_to[GB_random(ctl)];
2485                            GBS_chrcat(out, c);
2486                        }
2487                    }
2488                    else { // otherwise simply copy
2489                        GBS_strncat(out, seq+pos, count);
2490                    }
2491                    break;
2492            }
2493
2494            pos  += count;
2495            rest -= count;
2496        }
2497        inset = 1-inset; // toggle
2498    }
2499    return GBS_strclose(out);
2500}
2501
2502static GB_ERROR gbl_filter(GBL_command_arguments *args) {
2503    GBL_BEGIN_PARAMS;
2504    GBL_COMMON_FILTER_PARAMS;
2505
2506    filter_params param;
2507    GBL_STRUCT_PARAM_STRING(param, exclude, "exclude=", 0, "Exclude colums");
2508    GBL_STRUCT_PARAM_STRING(param, include, "include=", 0, "Include colums");
2509    param.function = FP_FILTER;
2510
2511    GBL_TRACE_PARAMS(args->cparam, args->vparam);
2512    GBL_END_PARAMS;
2513
2514    GBL_CHECK_FREE_PARAM(*args->coutput, args->cinput);
2515
2516    GB_ERROR error  = 0;
2517    int      inOrEx = !!param.include + !!param.exclude;
2518
2519    if (inOrEx != 1)    error = "Need exactly one parameter of: 'include', 'exclude'";
2520    else error                = apply_filters(args, &common_param, filter_seq, &param);
2521
2522    return error;
2523}
2524
2525static GB_ERROR gbl_change_gc(GBL_command_arguments *args) {
2526    GBL_BEGIN_PARAMS;
2527    GBL_COMMON_FILTER_PARAMS;
2528
2529    filter_params param;
2530    GBL_STRUCT_PARAM_STRING(param, exclude,   "exclude=", 0,    "Exclude colums");
2531    GBL_STRUCT_PARAM_STRING(param, include,   "include=", 0,    "Include colums");
2532    GBL_STRUCT_PARAM_INT   (param, change_pc, "change=",  0,    "percentage of changed columns (default: silently change nothing)");
2533    GBL_STRUCT_PARAM_STRING(param, change_to, "to=",      "GC", "change to one of this");
2534    param.function = FP_MODIFY;
2535
2536    GBL_TRACE_PARAMS(args->cparam, args->vparam);
2537    GBL_END_PARAMS;
2538
2539    GBL_CHECK_FREE_PARAM(*args->coutput, args->cinput);
2540
2541    GB_ERROR error  = 0;
2542    int      inOrEx = !!param.include + !!param.exclude;
2543
2544    if (inOrEx != 1) error = "Need exactly one parameter of: 'include', 'exclude'";
2545    else {
2546        error = apply_filters(args, &common_param, filter_seq, &param);
2547    }
2548
2549    return error;
2550}
2551
2552static GB_ERROR gbl_exec(GBL_command_arguments *args)
2553{
2554    GB_ERROR error = 0;
2555
2556    if (args->cparam==0) {
2557        error = "exec needs parameters:\nexec(command,...)";
2558    }
2559    else {
2560        // write inputstreams to temp file:
2561        char *inputname;
2562        int i;
2563        {
2564            char *filename = GB_unique_filename("arb_exec_input", "tmp");
2565            FILE *out      = GB_fopen_tempfile(filename, "wt", &inputname);
2566
2567            if (!out) error = GB_await_error();
2568            else {
2569                for (i=0; i<args->cinput; i++) {               // go through all in streams
2570                    fprintf(out, "%s\n", args->vinput[i].str);
2571                }
2572                fclose(out);
2573            }
2574            free(filename);
2575        }
2576
2577        if (!error) {
2578            // build shell command to execute
2579            char *sys;
2580            {
2581                GBS_strstruct *str = GBS_stropen(1000);
2582
2583                GBS_strcat(str, args->vparam[0].str);
2584                for (i=1; i<args->cparam; i++) {   // go through all in params
2585                    GBS_strcat(str, " \'");
2586                    GBS_strcat(str, args->vparam[i].str);
2587                    GBS_chrcat(str, '\'');
2588                }
2589                GBS_strcat(str, " <");
2590                GBS_strcat(str, inputname);
2591
2592                sys = GBS_strclose(str);
2593            }
2594
2595            char *result = 0;
2596            {
2597                FILE *in = popen(sys, "r");
2598                if (in) {
2599                    GBS_strstruct *str = GBS_stropen(4096);
2600
2601                    while ((i=getc(in)) != EOF) { GBS_chrcat(str, i); }
2602                    result = GBS_strclose(str);
2603                    pclose(in);
2604                }
2605                else {
2606                    error = GBS_global_string("Cannot execute shell command '%s'", sys);
2607                }
2608            }
2609
2610            if (!error) {
2611                gb_assert(result);
2612                PASS_2_OUT(args, result);
2613            }
2614
2615            free(sys);
2616        }
2617
2618        gb_assert(GB_is_privatefile(inputname, false));
2619        GB_unlink_or_warn(inputname, &error);
2620        free(inputname);
2621    }
2622
2623    return error;
2624}
2625
2626
2627static GBL_command_table gbl_command_table[] = {
2628    { "caps",            gbl_string_convert },
2629    { "change",          gbl_change_gc },
2630    { "checksum",        gbl_check },
2631    { "command",         gbl_command },
2632    { "compare",         gbl_compare },
2633    { "icompare",        gbl_icompare },
2634    { "contains",        gbl_contains },
2635    { "icontains",       gbl_icontains },
2636    { "count",           gbl_count },
2637    { "crop",            gbl_crop },
2638    { "cut",             gbl_cut },
2639    { "dd",              gbl_dd },
2640    { "define",          gbl_define },
2641    { "diff",            gbl_diff },
2642    { "div",             gbl_div },
2643    { "do",              gbl_do },
2644    { "drop",            gbl_drop },
2645    { "dropempty",       gbl_dropempty },
2646    { "dropzero",        gbl_dropzero },
2647    { "echo",            gbl_echo },
2648    { "equals",          gbl_equals },
2649    { "iequals",         gbl_iequals },
2650    { "eval",            gbl_eval },
2651    { "exec",            gbl_exec },
2652    { "export_sequence", gbl_export_sequence },
2653    { "extract_sequence", gbl_extract_sequence },
2654    { "extract_words",   gbl_extract_words },
2655    { "filter",          gbl_filter },
2656    { "format",          gbl_format_sequence },
2657    { "format_sequence", gbl_format_sequence },
2658    { "gcgchecksum",     gbl_gcgcheck },
2659    { "head",            gbl_head },
2660    { "keep",            gbl_keep },
2661    { "left",            gbl_head },
2662    { "len",             gbl_len },
2663    { "lower",           gbl_string_convert },
2664    { "merge",           gbl_merge },
2665    { "mid",             gbl_mid },
2666    { "mid0",            gbl_mid0 },
2667    { "minus",           gbl_minus },
2668    { "mult",            gbl_mult },
2669    { "origin_gene",     gbl_origin },
2670    { "origin_organism", gbl_origin },
2671    { "partof",          gbl_partof },
2672    { "ipartof",         gbl_ipartof },
2673    { "per_cent",        gbl_per_cent },
2674    { "plus",            gbl_plus },
2675    { "pretab",          gbl_pretab },
2676    { "readdb",          gbl_readdb },
2677    { "remove",          gbl_remove },
2678    { "rest",            gbl_rest },
2679    { "right",           gbl_tail },
2680    { "select",          gbl_select },
2681    { "sequence",        gbl_sequence },
2682    { "sequence_type",   gbl_sequence_type },
2683    { "split",           gbl_split },
2684    { "srt",             gbl_srt },
2685    { "streams",         gbl_streams },
2686    { "swap",            gbl_swap },
2687    { "tab",             gbl_tab },
2688    { "tail",            gbl_tail },
2689    { "taxonomy",        gbl_taxonomy },
2690    { "toback",          gbl_toback },
2691    { "tofront",         gbl_tofront },
2692    { "trace",           gbl_trace },
2693    { "translate",       gbl_translate },
2694    { "upper",           gbl_string_convert },
2695    { 0, 0 }
2696
2697};
2698
2699void gbl_install_standard_commands(GBDATA *gb_main) {
2700    gb_install_command_table(gb_main, gbl_command_table, ARRAY_ELEMS(gbl_command_table));
2701}
Note: See TracBrowser for help on using the repository browser.