root/trunk/ARBDB/adtools.cxx

Revision 8650, 32.2 KB (checked in by westram, 5 weeks ago)
  • gb_read_in_long / gb_write_out_long -> gb_read_in_uint32 / gb_write_out_uint32 (that's what it does)
    • changed parameter/result to uint32_t
    • inlined
    • uses fread/fwrite
  • gb_read_number
    • moved into ad_load.cxx
    • inlined
  • GBT_check_arb_file
    • moved into ad_load.cxx (due to inlining of gb_read_in_uint32)
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : adtools.cxx                                       //
4//   Purpose   : misc functions                                    //
5//                                                                 //
6//   Institute of Microbiology (Technical University Munich)       //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11#include "gb_data.h"
12#include "gb_main.h"
13
14#include <arbdbt.h>
15
16#include <arb_sort.h>
17#include <arb_str.h>
18#include <arb_strarray.h>
19
20#include <algorithm>
21
22using namespace std;
23
24GBDATA *GBT_create(GBDATA *father, const char *key, long delete_level) {
25    GBDATA *gbd = GB_create_container(father, key);
26    if (gbd) {
27        GB_ERROR error = GB_write_security_delete(gbd, delete_level);
28        if (error) {
29            GB_export_error(error);
30            gbd = NULL; // assuming caller will abort the transaction
31        }
32    }
33    return gbd;
34}
35
36GBDATA *GBT_find_or_create(GBDATA *father, const char *key, long delete_level) {
37    GBDATA *gbd = GB_entry(father, key);
38    if (!gbd) gbd = GBT_create(father, key, delete_level);
39    return gbd;
40}
41
42
43/* the following functions were meant to use user defined values.
44 *
45 * Especially for 'ECOLI' there is already a possibility to
46 * specify a different reference in edit4, but there's no
47 * data model in the DB for it. Consider whether it makes sense,
48 * if secedit uses it as well.
49 *
50 * Note: Don't change the result type to 'const char *' even if the functions always
51 *       return the same atm. That may change.
52 */
53
54char *GBT_get_default_helix   (GBDATA *) { return strdup("HELIX"); }
55char *GBT_get_default_helix_nr(GBDATA *) { return strdup("HELIX_NR"); }
56char *GBT_get_default_ref     (GBDATA *) { return strdup("ECOLI"); }
57
58
59// ----------------
60//      scan DB
61
62#define GBT_SUM_LEN 4096                            // maximum length of path
63
64struct GB_DbScanner : virtual Noncopyable {
65    GB_HASH   *hash_table;
66    StrArray&  result; // not owned!
67    GB_TYPES   type;
68    char      *buffer;
69
70    GB_DbScanner(StrArray& result_)
71        : result(result_)
72    {
73        hash_table = GBS_create_hash(1024, GB_MIND_CASE);
74        buffer     = (char*)malloc(GBT_SUM_LEN);
75        buffer[0]  = 0;
76    }
77
78    ~GB_DbScanner() {
79        GBS_free_hash(hash_table);
80        free(buffer);
81    }
82};
83
84static void gbt_scan_db_rek(GBDATA *gbd, char *prefix, int deep, GB_DbScanner *scanner) {
85    GB_TYPES type = GB_read_type(gbd);
86    GBDATA *gb2;
87    const char *key;
88    int len_of_prefix;
89    if (type == GB_DB) {
90        len_of_prefix = strlen(prefix);
91        for (gb2 = GB_child(gbd); gb2; gb2 = GB_nextChild(gb2)) {  // find everything
92            if (deep) {
93                key = GB_read_key_pntr(gb2);
94                sprintf(&prefix[len_of_prefix], "/%s", key);
95                gbt_scan_db_rek(gb2, prefix, 1, scanner);
96            }
97            else {
98                prefix[len_of_prefix] = 0;
99                gbt_scan_db_rek(gb2, prefix, 1, scanner);
100            }
101        }
102        prefix[len_of_prefix] = 0;
103    }
104    else {
105        if (GB_check_hkey(prefix+1)) {
106            GB_clear_error(); // occurs with internal keys (e.g. '@name'). @@@ should be handled in advance
107        }
108        else {
109            prefix[0] = (char)type;
110            GBS_incr_hash(scanner->hash_table, prefix);
111        }
112    }
113}
114
115struct scan_db_insert {
116    GB_DbScanner *scanner;
117    const char   *datapath;
118};
119
120static long gbs_scan_db_insert(const char *key, long val, void *cd_insert_data) {
121    scan_db_insert *insert    = (scan_db_insert *)cd_insert_data;
122    char           *to_insert = 0;
123
124    if (!insert->datapath) {
125        to_insert = strdup(key);
126    }
127    else {
128        if (ARB_strscmp(insert->datapath, key+1) == 0) { // datapath matches
129            to_insert    = strdup(key+strlen(insert->datapath)); // cut off prefix
130            to_insert[0] = key[0]; // copy type
131        }
132    }
133
134    if (to_insert) {
135        GB_DbScanner *scanner = insert->scanner;
136        scanner->result.put(to_insert);
137    }
138
139    return val;
140}
141
142static int gbs_scan_db_compare(const void *left, const void *right, void *) {
143    return strcmp((GB_CSTR)left+1, (GB_CSTR)right+1);
144}
145
146
147void GBT_scan_db(StrArray& fieldNames, GBDATA *gbd, const char *datapath) {
148    /*! scan CONTAINER for existing sub-keys
149     *
150     * recurses completely downwards the DB tree
151     *
152     * @param fieldNames gets filled with result strings
153     * - each string is the path to a node beyond gbd
154     * - every string exists only once
155     * - the first character of a string is the type of the entry
156     * - the strings are sorted alphabetically
157     * @param gbd node where search starts
158     * @param datapath if != NULL, only keys with prefix datapath are scanned and
159     * the prefix is removed from the resulting key_names.
160     *
161     * Note: this function is incredibly slow when called from clients
162     */
163
164    {
165        GB_DbScanner scanner(fieldNames);
166        gbt_scan_db_rek(gbd, scanner.buffer, 0, &scanner);
167        scan_db_insert insert = { &scanner, datapath, };
168        GBS_hash_do_loop(scanner.hash_table, gbs_scan_db_insert, &insert);
169    }
170    fieldNames.sort(gbs_scan_db_compare, 0);
171}
172
173// --------------------------
174//      message injection
175
176static void new_gbt_message_created_cb(GBDATA *gb_pending_messages, int */*cd*/, GB_CB_TYPE /*cbt*/) {
177    static int avoid_deadlock = 0;
178
179    if (!avoid_deadlock) {
180        GBDATA *gb_msg;
181
182        avoid_deadlock++;
183        GB_push_transaction(gb_pending_messages);
184
185        for (gb_msg = GB_entry(gb_pending_messages, "msg"); gb_msg;) {
186            {
187                const char *msg = GB_read_char_pntr(gb_msg);
188                GB_warning(msg);
189            }
190            {
191                GBDATA *gb_next_msg = GB_nextEntry(gb_msg);
192                GB_delete(gb_msg);
193                gb_msg              = gb_next_msg;
194            }
195        }
196
197        GB_pop_transaction(gb_pending_messages);
198        avoid_deadlock--;
199    }
200}
201
202void GBT_install_message_handler(GBDATA *gb_main) {
203    GBDATA *gb_pending_messages;
204
205    GB_push_transaction(gb_main);
206    gb_pending_messages = GB_search(gb_main, ERROR_CONTAINER_PATH, GB_CREATE_CONTAINER);
207    gb_assert(gb_pending_messages);
208    GB_add_callback(gb_pending_messages, GB_CB_SON_CREATED, new_gbt_message_created_cb, 0);
209    GB_pop_transaction(gb_main);
210
211#if defined(DEBUG) && 0
212    GBT_message(GB_get_root(gb_pending_messages), GBS_global_string("GBT_install_message_handler installed for gb_main=%p", gb_main));
213#endif // DEBUG
214}
215
216
217void GBT_message(GBDATA *gb_main, const char *msg) {
218    /*! When called in client(or server) this causes the DB server to show the message.
219     * Message is showed via GB_warning (which uses aw_message in GUIs)
220     *
221     * Note: The message is not shown before the transaction ends.
222     * If the transaction is aborted, the message is never shown!
223     *
224     * see also : GB_warning()
225     */
226
227    GB_ERROR error = GB_push_transaction(gb_main);
228
229    if (!error) {
230        GBDATA *gb_pending_messages = GB_search(gb_main, ERROR_CONTAINER_PATH, GB_CREATE_CONTAINER);
231        GBDATA *gb_msg              = gb_pending_messages ? GB_create(gb_pending_messages, "msg", GB_STRING) : 0;
232
233        if (!gb_msg) error = GB_await_error();
234        else {
235            gb_assert(msg);
236            error = GB_write_string(gb_msg, msg);
237        }
238    }
239    error = GB_end_transaction(gb_main, error);
240
241    if (error) {
242        fprintf(stderr, "GBT_message: Failed to write message '%s'\n(Reason: %s)\n", msg, error);
243    }
244}
245
246char *GBT_read_string(GBDATA *gb_container, const char *fieldpath) {
247    /*! Read value from database field (of type GB_STRING)
248     *
249     * @param gb_container where to start search for field
250     * @param fieldpath relative path from gb_container
251     *
252     * @return
253     * NULL in case of error (use GB_await_error()) or when field does not exist.
254     * otherwise returns a heap copy.
255     *
256     * other functions return a pointer to a temporary variable (invalidated by next call)
257     */
258
259    GBDATA *gbd;
260    char   *result = NULL;
261
262    GB_push_transaction(gb_container);
263    gbd = GB_search(gb_container, fieldpath, GB_FIND);
264    if (gbd) result = GB_read_string(gbd);
265    GB_pop_transaction(gb_container);
266    return result;
267}
268
269char *GBT_read_as_string(GBDATA *gb_container, const char *fieldpath) {
270    /*! like GBT_read_string()
271     *
272     * but
273     * - field may be of any type (result gets converted to reasonable char*)
274     */
275
276    GBDATA *gbd;
277    char   *result = NULL;
278
279    GB_push_transaction(gb_container);
280    gbd = GB_search(gb_container, fieldpath, GB_FIND);
281    if (gbd) result = GB_read_as_string(gbd);
282    GB_pop_transaction(gb_container);
283    return result;
284}
285
286const char *GBT_read_char_pntr(GBDATA *gb_container, const char *fieldpath) {
287    /*! like GBT_read_string()
288     *
289     * but
290     * - result is not a heap-copy and may be invalidated by further DB access
291     *   (especially by overwriting that fields)
292     *
293     * Note: Under no circumstances you may modify the result!
294     */
295
296    GBDATA     *gbd;
297    const char *result = NULL;
298
299    GB_push_transaction(gb_container);
300    gbd = GB_search(gb_container, fieldpath, GB_FIND);
301    if (gbd) result = GB_read_char_pntr(gbd);
302    GB_pop_transaction(gb_container);
303    return result;
304}
305
306NOT4PERL long *GBT_read_int(GBDATA *gb_container, const char *fieldpath) {
307    /*! similar to GBT_read_string()
308     *
309     * but
310     * - for fields of type GB_INT
311     * - result gets invalidated by next call
312     */
313
314    GBDATA *gbd;
315    long   *result = NULL;
316
317    GB_push_transaction(gb_container);
318    gbd = GB_search(gb_container, fieldpath, GB_FIND);
319    if (gbd) {
320        static long result_var;
321        result_var = GB_read_int(gbd);
322        result     = &result_var;
323    }
324    GB_pop_transaction(gb_container);
325    return result;
326}
327
328NOT4PERL double *GBT_read_float(GBDATA *gb_container, const char *fieldpath) {
329    /*! similar to GBT_read_string()
330     *
331     * but
332     * - for fields of type GB_FLOAT
333     * - result gets invalidated by next call
334     */
335
336    GBDATA *gbd;
337    double *result = NULL;
338
339    GB_push_transaction(gb_container);
340    gbd = GB_search(gb_container, fieldpath, GB_FIND);
341    if (gbd) {
342        static double result_var;
343        result_var = GB_read_float(gbd);
344        result     = &result_var;
345    }
346    GB_pop_transaction(gb_container);
347    return result;
348}
349
350char *GBT_readOrCreate_string(GBDATA *gb_container, const char *fieldpath, const char *default_value) {
351    /*! like GBT_read_string(),
352     *
353     * but if field does not exist, it will be created and initialized with 'default_value'
354     */
355    GBDATA *gb_string;
356    char   *result = NULL;
357
358    GB_push_transaction(gb_container);
359    gb_string             = GB_searchOrCreate_string(gb_container, fieldpath, default_value);
360    if (gb_string) result = GB_read_string(gb_string);
361    GB_pop_transaction(gb_container);
362    return result;
363}
364
365const char *GBT_readOrCreate_char_pntr(GBDATA *gb_container, const char *fieldpath, const char *default_value) {
366    /*! like GBT_read_char_pntr(),
367     *
368     * but if field does not exist, it will be created and initialized with 'default_value'
369     */
370
371    GBDATA     *gb_string;
372    const char *result = NULL;
373
374    GB_push_transaction(gb_container);
375    gb_string             = GB_searchOrCreate_string(gb_container, fieldpath, default_value);
376    if (gb_string) result = GB_read_char_pntr(gb_string);
377    GB_pop_transaction(gb_container);
378    return result;
379}
380
381NOT4PERL long *GBT_readOrCreate_int(GBDATA *gb_container, const char *fieldpath, long default_value) {
382    /*! like GBT_read_int(),
383     *
384     * but if field does not exist, it will be created and initialized with 'default_value'
385     */
386
387    GBDATA *gb_int;
388    long   *result = NULL;
389
390    GB_push_transaction(gb_container);
391    gb_int = GB_searchOrCreate_int(gb_container, fieldpath, default_value);
392    if (gb_int) {
393        static long result_var;
394        result_var = GB_read_int(gb_int);
395        result     = &result_var;
396    }
397    GB_pop_transaction(gb_container);
398    return result;
399}
400
401NOT4PERL double *GBT_readOrCreate_float(GBDATA *gb_container, const char *fieldpath, double default_value) {
402    /*! like GBT_read_float(),
403     *
404     * but if field does not exist, it will be created and initialized with 'default_value'
405     */
406
407    gb_assert(default_value == default_value); // !nan
408   
409    GBDATA *gb_float;
410    double *result = NULL;
411
412    GB_push_transaction(gb_container);
413    gb_float = GB_searchOrCreate_float(gb_container, fieldpath, default_value);
414    if (gb_float) {
415        static double result_var;
416        result_var = GB_read_float(gb_float);
417        result     = &result_var;
418    }
419    else {
420        gb_assert(0);
421    }
422    GB_pop_transaction(gb_container);
423    return result;
424}
425
426// -------------------------------------------------------------------
427//      overwrite existing or create new database field
428//      (field must not exist twice or more - it has to be unique!!)
429
430GB_ERROR GBT_write_string(GBDATA *gb_container, const char *fieldpath, const char *content) {
431    /*! Write content to database field of type GB_STRING
432     *
433     * if field exists, it will be overwritten.
434     * if field does not exist, it will be created.
435     *
436     * The field should be unique, i.e. it should not exist twice or more in it's parent container
437     *
438     * @return GB_ERROR on failure
439     */
440    GB_ERROR  error = GB_push_transaction(gb_container);
441    GBDATA   *gbd   = GB_search(gb_container, fieldpath, GB_STRING);
442    if (!gbd) error = GB_await_error();
443    else {
444        error = GB_write_string(gbd, content);
445        gb_assert(GB_nextEntry(gbd) == 0); // only one entry should exist (sure you want to use this function?)
446    }
447    return GB_end_transaction(gb_container, error);
448}
449
450GB_ERROR GBT_write_int(GBDATA *gb_container, const char *fieldpath, long content) {
451    /*! like GBT_write_string(),
452     *
453     * but for fields of type GB_INT
454     */
455    GB_ERROR  error = GB_push_transaction(gb_container);
456    GBDATA   *gbd   = GB_search(gb_container, fieldpath, GB_INT);
457    if (!gbd) error = GB_await_error();
458    else {
459        error = GB_write_int(gbd, content);
460        gb_assert(GB_nextEntry(gbd) == 0); // only one entry should exist (sure you want to use this function?)
461    }
462    return GB_end_transaction(gb_container, error);
463}
464
465GB_ERROR GBT_write_byte(GBDATA *gb_container, const char *fieldpath, unsigned char content) {
466    /*! like GBT_write_string(),
467     *
468     * but for fields of type GB_BYTE
469     */
470    GB_ERROR  error = GB_push_transaction(gb_container);
471    GBDATA   *gbd   = GB_search(gb_container, fieldpath, GB_BYTE);
472    if (!gbd) error = GB_await_error();
473    else {
474        error = GB_write_byte(gbd, content);
475        gb_assert(GB_nextEntry(gbd) == 0); // only one entry should exist (sure you want to use this function?)
476    }
477    return GB_end_transaction(gb_container, error);
478}
479
480
481GB_ERROR GBT_write_float(GBDATA *gb_container, const char *fieldpath, double content) {
482    /*! like GBT_write_string(),
483     *
484     * but for fields of type GB_FLOAT
485     */
486
487    gb_assert(content == content); // !nan
488
489    GB_ERROR  error = GB_push_transaction(gb_container);
490    GBDATA   *gbd   = GB_search(gb_container, fieldpath, GB_FLOAT);
491    if (!gbd) error = GB_await_error();
492    else {
493        error = GB_write_float(gbd, content);
494        gb_assert(GB_nextEntry(gbd) == 0); // only one entry should exist (sure you want to use this function?)
495    }
496    return GB_end_transaction(gb_container, error);
497}
498
499
500static GBDATA *GB_test_link_follower(GBDATA *gb_main, GBDATA *gb_link, const char *link) {
501    GBDATA *linktarget = GB_search(gb_main, "tmp/link/string", GB_STRING);
502    // GBUSE(gb_link);
503    GB_write_string(linktarget, GBS_global_string("Link is '%s'", link));
504    return GB_get_father(linktarget);
505}
506
507// --------------------
508//      save & load
509
510GBDATA *GBT_open(const char *path, const char *opent) {
511    /*! Open a database,
512     *  create an index for species and extended names (server only!) and
513     *  disable saving in the PT_SERVER directory.
514     *
515     * @param path filename of the DB
516     * @param opent see GB_login()
517     * @return database handle (or NULL in which case an error is exported)
518     * @see GB_open()
519     */
520
521    GBDATA *gbd = GB_open(path, opent);
522    if (gbd) {
523        GB_disable_path(gbd, GB_path_in_ARBLIB("pts/*"));
524        GB_ERROR error = NULL;
525        {
526            GB_transaction ta(gbd);
527
528            if (!strchr(path, ':')) {
529                GBDATA *species_data = GB_search(gbd, "species_data", GB_FIND); // do NOT create species_data here!
530                if (species_data) {
531                    long hash_size = max(GB_number_of_subentries(species_data), GBT_SPECIES_INDEX_SIZE);
532                    error          = GB_create_index(species_data, "name", GB_IGNORE_CASE, hash_size);
533
534                    if (!error) {
535                        GBDATA *extended_data = GBT_get_SAI_data(gbd);
536                        hash_size             = max(GB_number_of_subentries(extended_data), GBT_SAI_INDEX_SIZE);
537                        error                 = GB_create_index(extended_data, "name", GB_IGNORE_CASE, hash_size);
538                    }
539                }
540            }
541            if (!error) {
542                GBDATA *gb_tmp = GB_search(gbd, "tmp", GB_CREATE_CONTAINER);
543                if (gb_tmp) error = GB_set_temporary(gb_tmp);
544            }
545
546            if (!error) {
547                {
548                    GB_MAIN_TYPE *Main = GB_MAIN(gbd);
549                    Main->table_hash = GBS_create_hash(256, GB_MIND_CASE);
550                    GB_install_link_follower(gbd, "REF", GB_test_link_follower);
551                }
552                GBT_install_table_link_follower(gbd);
553            }
554        }
555        if (error) {
556            GB_close(gbd);
557            gbd = NULL;
558            GB_export_error(error);
559        }
560    }
561    gb_assert(contradicted(gbd, GB_have_error()));
562    return gbd;
563}
564
565/* --------------------------------------------------------------------------------
566 * Remote commands
567 *
568 * Note: These commands may seem to be unused from inside ARB.
569 * They get used, but only indirectly via the macro-function.
570 *
571 * Search for
572 * - BIO::remote_action         (use of GBT_remote_action)
573 * - BIO::remote_awar           (use of GBT_remote_awar)
574 * - BIO::remote_read_awar      (use of GBT_remote_read_awar)
575 */
576
577
578#define AWAR_REMOTE_BASE_TPL            "tmp/remote/%s/"
579#define MAX_REMOTE_APPLICATION_NAME_LEN 30
580#define MAX_REMOTE_AWAR_STRING_LEN      (11+MAX_REMOTE_APPLICATION_NAME_LEN+1+6+1)
581
582struct remote_awars {
583    char awar_action[MAX_REMOTE_AWAR_STRING_LEN];
584    char awar_result[MAX_REMOTE_AWAR_STRING_LEN];
585    char awar_awar[MAX_REMOTE_AWAR_STRING_LEN];
586    char awar_value[MAX_REMOTE_AWAR_STRING_LEN];
587};
588
589static void gbt_build_remote_awars(remote_awars *awars, const char *application) {
590    int length;
591
592    gb_assert(strlen(application) <= MAX_REMOTE_APPLICATION_NAME_LEN);
593
594    length = sprintf(awars->awar_action, AWAR_REMOTE_BASE_TPL, application);
595    gb_assert(length < (MAX_REMOTE_AWAR_STRING_LEN-6)); // Note :  6 is length of longest name appended below !
596
597    strcpy(awars->awar_result, awars->awar_action);
598    strcpy(awars->awar_awar, awars->awar_action);
599    strcpy(awars->awar_value, awars->awar_action);
600
601    strcpy(awars->awar_action+length, "action");
602    strcpy(awars->awar_result+length, "result");
603    strcpy(awars->awar_awar+length,   "awar");
604    strcpy(awars->awar_value+length,  "value");
605}
606
607static GBDATA *gbt_remote_search_awar(GBDATA *gb_main, const char *awar_name) {
608    GBDATA *gb_action;
609    while (1) {
610        GB_begin_transaction(gb_main);
611        gb_action = GB_search(gb_main, awar_name, GB_FIND);
612        GB_commit_transaction(gb_main);
613        if (gb_action) break;
614        GB_usleep(2000);
615    }
616    return gb_action;
617}
618
619static GB_ERROR gbt_wait_for_remote_action(GBDATA *gb_main, GBDATA *gb_action, const char *awar_read) {
620    GB_ERROR error = 0;
621
622    // wait to end of action
623
624    while (!error) {
625        GB_usleep(2000);
626        error = GB_begin_transaction(gb_main);
627        if (!error) {
628            char *ac = GB_read_string(gb_action);
629            if (ac[0] == 0) { // action has been cleared from remote side
630                GBDATA *gb_result = GB_search(gb_main, awar_read, GB_STRING);
631                error             = GB_read_char_pntr(gb_result); // check for errors
632            }
633            free(ac);
634        }
635        error = GB_end_transaction(gb_main, error);
636    }
637
638    return error; // may be error or result
639}
640
641static void mark_as_macro_executor(GBDATA *gb_main, bool mark) {
642    // call this with 'mark' = true to mark yourself as "client running a macro"
643    // -> GB_close with automatically announce termination of this client to main DB (MACRO_TRIGGER_CONTAINER/terminated)
644    // do NOT call with 'mark' = false
645
646    static bool client_is_macro_executor = false;
647    if (mark) {
648        if (!client_is_macro_executor) {
649            GB_atclose(gb_main, (void (*)(GBDATA*, void*))mark_as_macro_executor, 0);
650            client_is_macro_executor = true;
651        }
652    }
653    else {
654        // called via GB_atclose callback (i.e. at end of currently executed macro file)
655        GB_ERROR error = NULL;
656        gb_assert(client_is_macro_executor);
657        if (client_is_macro_executor) {
658            GB_transaction ta(gb_main);
659
660            GBDATA *gb_terminated = GB_search(gb_main, MACRO_TRIGGER_CONTAINER "/terminated", GB_FIND);
661            gb_assert(gb_terminated); // should have been created by macro caller
662            if (gb_terminated) {
663                error = GB_write_int(gb_terminated, GB_read_int(gb_terminated)+1); // notify macro caller
664            }
665
666            error = ta.close(error);
667        }
668        if (error) GBT_message(gb_main, error);
669    }
670}
671
672GB_ERROR GBT_remote_action(GBDATA *gb_main, const char *application, const char *action_name) {
673    // needs to be public (needed by perl-macros)
674
675    mark_as_macro_executor(gb_main, true);
676   
677    remote_awars  awars;
678    GBDATA       *gb_action;
679    GB_ERROR      error = NULL;
680
681    gbt_build_remote_awars(&awars, application);
682    gb_action = gbt_remote_search_awar(gb_main, awars.awar_action);
683
684    error             = GB_begin_transaction(gb_main);
685    if (!error) error = GB_write_string(gb_action, action_name); // write command
686    error             = GB_end_transaction(gb_main, error);
687
688    if (!error) error = gbt_wait_for_remote_action(gb_main, gb_action, awars.awar_result);
689    return error;
690}
691
692GB_ERROR GBT_remote_awar(GBDATA *gb_main, const char *application, const char *awar_name, const char *value) {
693    // needs to be public (needed by perl-macros)
694
695    mark_as_macro_executor(gb_main, true);
696
697    remote_awars  awars;
698    GBDATA       *gb_awar;
699    GB_ERROR      error = NULL;
700
701    gbt_build_remote_awars(&awars, application);
702    gb_awar = gbt_remote_search_awar(gb_main, awars.awar_awar);
703
704    error = GB_begin_transaction(gb_main);
705    if (!error) error = GB_write_string(gb_awar, awar_name);
706    if (!error) error = GBT_write_string(gb_main, awars.awar_value, value);
707    error = GB_end_transaction(gb_main, error);
708
709    if (!error) error = gbt_wait_for_remote_action(gb_main, gb_awar, awars.awar_result);
710
711    return error;
712}
713
714GB_ERROR GBT_remote_read_awar(GBDATA *gb_main, const char *application, const char *awar_name) {
715    // needs to be public (needed by perl-macros)
716
717    mark_as_macro_executor(gb_main, true);
718
719    remote_awars  awars;
720    GBDATA       *gb_awar;
721    GB_ERROR      error = NULL;
722
723    gbt_build_remote_awars(&awars, application);
724    gb_awar = gbt_remote_search_awar(gb_main, awars.awar_awar);
725
726    error             = GB_begin_transaction(gb_main);
727    if (!error) error = GB_write_string(gb_awar, awar_name);
728    if (!error) error = GBT_write_string(gb_main, awars.awar_action, "AWAR_REMOTE_READ");
729    error             = GB_end_transaction(gb_main, error);
730
731    if (!error) error = gbt_wait_for_remote_action(gb_main, gb_awar, awars.awar_value);
732    return error;
733}
734
735// ---------------------------
736//      self-notification
737// ---------------------------
738// provides a mechanism to notify ARB after some external tool finishes
739
740#define ARB_NOTIFICATIONS "tmp/notify"
741
742/* DB structure for notifications :
743 *
744 * ARB_NOTIFICATIONS/counter        GB_INT      counts used ids
745 * ARB_NOTIFICATIONS/notify/id      GB_INT      id of notification
746 * ARB_NOTIFICATIONS/notify/message GB_STRING   message of notification (set by callback)
747 */
748
749typedef void (*notify_cb_type)(const char *message, void *client_data);
750
751struct NotifyCb {
752    notify_cb_type  cb;
753    void           *client_data;
754};
755
756static void notify_cb(GBDATA *gb_message, int *cb_info, GB_CB_TYPE cb_type) {
757    if (cb_type != GB_CB_DELETE) {
758        GB_remove_callback(gb_message, GB_CB_TYPE(GB_CB_CHANGED|GB_CB_DELETE), notify_cb, cb_info);
759    }
760
761    int       cb_done = 0;
762    NotifyCb *pending = (NotifyCb*)cb_info;
763
764    if (cb_type == GB_CB_CHANGED) {
765        GB_ERROR    error   = 0;
766        const char *message = GB_read_char_pntr(gb_message);
767        if (!message) error = GB_await_error();
768        else {
769            pending->cb(message, pending->client_data);
770            cb_done = 1;
771        }
772
773        if (!cb_done) {
774            gb_assert(error);
775            GB_warningf("Notification failed (Reason: %s)\n", error);
776            gb_assert(0);
777        }
778    }
779    else { // called from GB_remove_last_notification
780        gb_assert(cb_type == GB_CB_DELETE);
781    }
782
783    free(pending);
784}
785
786static int allocateNotificationID(GBDATA *gb_main, int *cb_info) {
787    /* returns a unique notification ID
788     * or 0 (use GB_get_error() in this case)
789     */
790
791    int      id    = 0;
792    GB_ERROR error = GB_push_transaction(gb_main);
793
794    if (!error) {
795        GBDATA *gb_notify = GB_search(gb_main, ARB_NOTIFICATIONS, GB_CREATE_CONTAINER);
796
797        if (gb_notify) {
798            GBDATA *gb_counter = GB_searchOrCreate_int(gb_notify, "counter", 0);
799
800            if (gb_counter) {
801                int newid = GB_read_int(gb_counter) + 1; // increment counter
802                error     = GB_write_int(gb_counter, newid);
803
804                if (!error) {
805                    // change transaction (to never use id twice!)
806                    error             = GB_pop_transaction(gb_main);
807                    if (!error) error = GB_push_transaction(gb_main);
808
809                    if (!error) {
810                        GBDATA *gb_notification = GB_create_container(gb_notify, "notify");
811
812                        if (gb_notification) {
813                            error = GBT_write_int(gb_notification, "id", newid);
814                            if (!error) {
815                                GBDATA *gb_message = GB_searchOrCreate_string(gb_notification, "message", "");
816
817                                if (gb_message) {
818                                    error = GB_add_callback(gb_message, GB_CB_TYPE(GB_CB_CHANGED|GB_CB_DELETE), notify_cb, cb_info);
819                                    if (!error) {
820                                        id = newid; // success
821                                    }
822                                }
823                            }
824                        }
825                    }
826                }
827            }
828        }
829    }
830
831    if (!id) {
832        if (!error) error = GB_await_error();
833        error = GBS_global_string("Failed to allocate notification ID (%s)", error);
834    }
835    error = GB_end_transaction(gb_main, error);
836    if (error) GB_export_error(error);
837
838    return id;
839}
840
841
842char *GB_generate_notification(GBDATA *gb_main,
843                               void (*cb)(const char *message, void *client_data),
844                               const char *message, void *client_data)
845{
846    /* generates a call to 'arb_notify', meant to be inserted into some external system call.
847     * When that call is executed, the callback instantiated here will be called.
848     *
849     * Tip : To return variable results from the shell script, use the name of an environment
850     *       variable in 'message' (e.g. "$RESULT")
851     */
852
853    int       id;
854    char     *arb_notify_call = 0;
855    NotifyCb *pending         = (NotifyCb*)malloc(sizeof(*pending));
856
857    pending->cb          = cb;
858    pending->client_data = client_data;
859
860    id = allocateNotificationID(gb_main, (int*)pending);
861    if (id) {
862        arb_notify_call = GBS_global_string_copy("arb_notify %i \"%s\"", id, message);
863    }
864    else {
865        free(pending);
866    }
867
868    return arb_notify_call;
869}
870
871GB_ERROR GB_remove_last_notification(GBDATA *gb_main) {
872    // aborts the last notification
873    GB_ERROR error = GB_push_transaction(gb_main);
874
875    if (!error) {
876        GBDATA *gb_notify = GB_search(gb_main, ARB_NOTIFICATIONS, GB_CREATE_CONTAINER);
877        if (gb_notify) {
878            GBDATA *gb_counter = GB_entry(gb_notify, "counter");
879            if (gb_counter) {
880                int     id    = GB_read_int(gb_counter);
881                GBDATA *gb_id = GB_find_int(gb_notify, "id", id, SEARCH_GRANDCHILD);
882
883                if (!gb_id) {
884                    error = GBS_global_string("No notification for ID %i", id);
885                    gb_assert(0);           // GB_generate_notification() has not been called for 'id'!
886                }
887                else {
888                    GBDATA *gb_message = GB_brother(gb_id, "message");
889
890                    if (!gb_message) {
891                        error = "Missing 'message' entry";
892                    }
893                    else {
894                        error = GB_delete(gb_message); // calls notify_cb
895                    }
896                }
897            }
898            else {
899                error = "No notification generated yet";
900            }
901        }
902    }
903
904    error = GB_end_transaction(gb_main, error);
905    return error;
906}
907
908GB_ERROR GB_notify(GBDATA *gb_main, int id, const char *message) {
909    /* called via 'arb_notify'
910     * 'id' has to be generated by GB_generate_notification()
911     * 'message' is passed to notification callback belonging to id
912     */
913
914    GB_ERROR  error     = 0;
915    GBDATA   *gb_notify = GB_search(gb_main, ARB_NOTIFICATIONS, GB_FIND);
916
917    if (!gb_notify) {
918        error = "Missing notification data";
919        gb_assert(0);           // GB_generate_notification() has not been called!
920    }
921    else {
922        GBDATA *gb_id = GB_find_int(gb_notify, "id", id, SEARCH_GRANDCHILD);
923
924        if (!gb_id) {
925            error = GBS_global_string("No notification for ID %i", id);
926        }
927        else {
928            GBDATA *gb_message = GB_brother(gb_id, "message");
929
930            if (!gb_message) {
931                error = "Missing 'message' entry";
932            }
933            else {
934                // callback the instantiating DB client
935                error = GB_write_string(gb_message, message);
936            }
937        }
938    }
939
940    return error;
941}
942
943// --------------------------------------------------------------------------------
944
945#ifdef UNIT_TESTS
946#include <test_unit.h>
947
948#define TEST_ASSERT_SCANNED_EQUALS(path,expected) do {  \
949        GB_transaction ta(gb_main);                     \
950        StrArray fields;                                \
951        GBT_scan_db(fields, gb_main, path);             \
952        char  *joined = GBT_join_names(fields, ',');    \
953        TEST_ASSERT_EQUAL(joined, expected);            \
954        free(joined);                                   \
955    } while (0)
956
957void TEST_scan_db() {
958    GB_shell  shell;
959    GBDATA   *gb_main = GB_open("TEST_loadsave.arb", "r");
960
961
962    TEST_ASSERT_SCANNED_EQUALS(NULL, 
963                               "alignment/aligned,alignment/alignment_len,alignment/alignment_name,alignment/alignment_rem,alignment/alignment_type,alignment/alignment_write_security,alignment/auto_format,"
964                               "byte,bytes,bytes,bytes,"
965                               "extended/acc,extended/ali_16s/data,extended/aligned,extended/errors,extended/full_name,extended/name,"
966                               "\nfloats,\tints,\tints_empty,"
967                               "key_data/key/key_hidden,key_data/key/key_name,key_data/key/key_type,"
968                               "species/AL,species/ARB_color,species/acc,species/ali_16s/data,species/bits_test,species/float_test,species/full_name,species/name,species/seqcheck,species/tax,"
969                               "str,str_percent,use");
970
971    TEST_ASSERT_SCANNED_EQUALS("species", "/AL,/ARB_color,/acc,/ali_16s/data,/bits_test,/float_test,/full_name,/name,/seqcheck,/tax");
972
973    TEST_ASSERT(!GB_have_error());
974   
975    GB_close(gb_main);
976}
977
978#endif // UNIT_TESTS
Note: See TracBrowser for help on using the browser.