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 | |
---|
17 | GBDATA *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 | |
---|
39 | char *GBT_get_default_helix(GBDATA *gb_main) { |
---|
40 | GBUSE(gb_main); |
---|
41 | return strdup("HELIX"); |
---|
42 | } |
---|
43 | |
---|
44 | char *GBT_get_default_helix_nr(GBDATA *gb_main) { |
---|
45 | GBUSE(gb_main); |
---|
46 | return strdup("HELIX_NR"); |
---|
47 | } |
---|
48 | |
---|
49 | char *GBT_get_default_ref(GBDATA *gb_main) { |
---|
50 | GBUSE(gb_main); |
---|
51 | return strdup("ECOLI"); |
---|
52 | } |
---|
53 | |
---|
54 | |
---|
55 | /******************************************************************************************** |
---|
56 | check routines |
---|
57 | ********************************************************************************************/ |
---|
58 | GB_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 | |
---|
90 | struct DbScanner { |
---|
91 | GB_HASH *hash_table; |
---|
92 | int count; |
---|
93 | char **result; |
---|
94 | GB_TYPES type; |
---|
95 | char *buffer; |
---|
96 | }; |
---|
97 | |
---|
98 | static 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 | |
---|
129 | static 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 | |
---|
138 | struct scan_db_insert { |
---|
139 | struct DbScanner *scanner; |
---|
140 | const char *datapath; |
---|
141 | }; |
---|
142 | |
---|
143 | static 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 | |
---|
165 | static 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 | |
---|
171 | char **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 | |
---|
213 | static 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 | |
---|
242 | void 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 | |
---|
256 | void 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 | |
---|
297 | char **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 | |
---|
331 | char *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 | |
---|
348 | void 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 | |
---|
363 | char *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 | |
---|
374 | char *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 | |
---|
385 | const 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 | |
---|
396 | NOT4PERL 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 | |
---|
411 | NOT4PERL 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 | |
---|
431 | char *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 | |
---|
442 | const 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 | |
---|
453 | NOT4PERL 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 | |
---|
468 | NOT4PERL 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 | |
---|
487 | GB_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 | |
---|
498 | GB_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 | |
---|
509 | GB_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 | |
---|
521 | GB_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 | |
---|
533 | GBDATA *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 | |
---|
547 | GBDATA *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 | |
---|
603 | struct 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 | |
---|
610 | static 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 | |
---|
628 | static 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 | |
---|
640 | static 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 | |
---|
662 | GB_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 | |
---|
678 | GB_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 | |
---|
696 | GB_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 | |
---|
713 | const 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 | |
---|
745 | typedef void (*notify_cb_type)(const char *message, void *client_data); |
---|
746 | |
---|
747 | struct NCB { |
---|
748 | notify_cb_type cb; |
---|
749 | void *client_data; |
---|
750 | }; |
---|
751 | |
---|
752 | static 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 | |
---|
780 | static 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 | |
---|
836 | char *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 | |
---|
865 | GB_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 | |
---|
902 | GB_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 | |
---|