source: tags/arb_5.1/ARBDB/adtools.c

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