source: tags/arb_5.5/ARBDB/adlang1.c

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