ARB
db_browser.cxx
Go to the documentation of this file.
1 // ==================================================================== //
2 // //
3 // File : db_browser.cxx //
4 // Purpose : Simple database viewer //
5 // //
6 // //
7 // Coded by Ralf Westram (coder@reallysoft.de) in May 2004 //
8 // Copyright Department of Microbiology (Technical University Munich) //
9 // //
10 // Visit our web site at: http://www.arb-home.de/ //
11 // //
12 // ==================================================================== //
13 
14 #include "hexdump.hxx"
15 #include "app.hxx"
16 
17 #include <aw_window.hxx>
18 #include <aw_msg.hxx>
19 #include <aw_awar.hxx>
20 #include <aw_select.hxx>
21 #include <aw_system.hxx>
22 #include <aw_advice.hxx>
23 
24 #include <arbdbt.h>
25 
26 #include <arb_str.h>
27 #include <arb_strarray.h>
28 
29 #include <arb_misc.h>
30 #include <arb_diff.h>
31 #include <arb_file.h>
32 #include <arb_sleep.h>
33 #include <ad_cb.h>
34 
35 #include <string>
36 #include <vector>
37 #include <map>
38 #include <algorithm>
39 
40 // do includes above (otherwise depends depend on DEBUG)
41 #if defined(DEBUG)
42 
43 using namespace std;
44 
45 // used AWARs :
46 
47 #define AWAR_DBB_BASE "/dbbrowser"
48 #define AWAR_DBB_TMP_BASE "/tmp" AWAR_DBB_BASE
49 
50 #define AWAR_DBB_DB AWAR_DBB_BASE "/db"
51 #define AWAR_DBB_ORDER AWAR_DBB_BASE "/order"
52 #define AWAR_DBB_PATH AWAR_DBB_BASE "/path"
53 #define AWAR_DBB_HISTORY AWAR_DBB_BASE "/history"
54 
55 #define AWAR_DBB_BROWSE AWAR_DBB_TMP_BASE "/browse"
56 #define AWAR_DBB_INFO AWAR_DBB_TMP_BASE "/info"
57 
58 #define AWAR_DBB_RECSIZE AWAR_DBB_BASE "/recsize"
59 
60 #define AWAR_DUMP_HEX AWAR_DBB_BASE "/hex"
61 #define AWAR_DUMP_ASCII AWAR_DBB_BASE "/ascii"
62 #define AWAR_DUMP_SPACE AWAR_DBB_BASE "/space"
63 #define AWAR_DUMP_WIDTH AWAR_DBB_BASE "/width"
64 #define AWAR_DUMP_BLOCK AWAR_DBB_BASE "/block"
65 
66 #define HISTORY_PSEUDO_PATH "*history*"
67 #define ENTRY_MAX_LENGTH 1000
68 #define HISTORY_MAX_LENGTH 20000
69 
70 inline bool is_dbbrowser_pseudo_path(const char *path) {
71  return
72  path &&
73  path[0] == '*' &&
74  strcmp(path, HISTORY_PSEUDO_PATH) == 0;
75 }
76 
77 enum SortOrder {
78  SORT_NONE,
79  SORT_NAME,
80  SORT_CONTAINER,
81  SORT_OCCUR,
82  SORT_TYPE,
83  SORT_CONTENT,
84 
85  SORT_COUNT
86 };
87 
88 static const char *sort_order_name[SORT_COUNT] = {
89  "Unsorted",
90  "Name",
91  "Name (Container)",
92  "Name (Occur)",
93  "Type",
94  "Content",
95 };
96 
97 // used to sort entries in list
98 struct list_entry {
99  const char *key_name;
100  GB_TYPES type;
101  int childCount; // -1 if only one child with key_name exists
102  GBDATA *gbd;
103  string content;
104 
105  static SortOrder sort_order;
106 
107  inline bool less_than_by_name(const list_entry& other) const {
108  int cmp = ARB_stricmp(key_name, other.key_name);
109  if (cmp != 0) return cmp<0; // name differs!
110  return childCount<other.childCount; // equal names -> compare child count
111  }
112 
113  inline int cmp_by_container(const list_entry& other) const { return int(type != GB_DB) - int(other.type != GB_DB); }
114  inline int cmp_by_childcount(const list_entry& other) const { return childCount - other.childCount; }
115 
116  inline bool less_than_by_container_name(const list_entry& other) const {
117  int cmp = cmp_by_container(other);
118  if (cmp == 0) return less_than_by_name(other);
119  return cmp<0;
120  }
121  inline bool less_than_by_childcount_name(const list_entry& other) const {
122  int cmp = cmp_by_childcount(other);
123  if (cmp == 0) return less_than_by_name(other);
124  return cmp<0;
125  }
126 
127  bool operator<(const list_entry& other) const {
128  bool is_less = false;
129  switch (sort_order) {
130  case SORT_COUNT: break;
131  case SORT_NONE:
132  arb_assert(0); // not possible
133  break;
134 
135  case SORT_NAME:
136  is_less = less_than_by_name(other);
137  break;
138 
139  case SORT_CONTAINER:
140  is_less = less_than_by_container_name(other);
141  break;
142 
143  case SORT_OCCUR:
144  is_less = less_than_by_childcount_name(other);
145  break;
146 
147  case SORT_CONTENT: {
148  int cmp = ARB_stricmp(content.c_str(), other.content.c_str());
149 
150  if (cmp != 0) is_less = cmp<0;
151  else is_less = less_than_by_container_name(other);
152 
153  break;
154  }
155  case SORT_TYPE: {
156  int cmp = type-other.type;
157 
158  if (cmp == 0) is_less = less_than_by_name(other);
159  else is_less = cmp<0;
160 
161  break;
162  }
163  }
164  return is_less;
165  }
166 };
167 
168 SortOrder list_entry::sort_order = SORT_NONE;
169 
170 // ---------------------
171 // create AWARs
172 
173 static MemDump make_userdefined_MemDump(AW_root *awr) {
174  bool hex = awr->awar(AWAR_DUMP_HEX)->read_int();
175  bool ascii = awr->awar(AWAR_DUMP_ASCII)->read_int();
176  bool space = awr->awar(AWAR_DUMP_SPACE)->read_int();
177  size_t width = awr->awar(AWAR_DUMP_WIDTH)->read_int();
178  size_t separate = awr->awar(AWAR_DUMP_BLOCK)->read_int();
179 
180  bool offset = (hex||ascii) && width;
181 
182  return MemDump(offset, hex, ascii, width, separate, space);
183 }
184 
185 static void nodedisplay_changed_cb(AW_root *aw_root) {
186  aw_root->awar(AWAR_DBB_BROWSE)->touch();
187 }
188 
189 void AWT_create_db_browser_awars(AW_root *aw_root, AW_default aw_def) {
190  aw_root->awar_int (AWAR_DBB_DB, 0, aw_def); // index to internal order of announced databases
191  aw_root->awar_int (AWAR_DBB_ORDER, SORT_CONTAINER, aw_def); // sort order for "browse"-box
192  aw_root->awar_string(AWAR_DBB_PATH, "/", aw_def); // path in database
193  aw_root->awar_string(AWAR_DBB_BROWSE, "", aw_def); // selection in browser (= child name)
194  aw_root->awar_string(AWAR_DBB_INFO, "<select an element>", aw_def); // information about selected item
195  aw_root->awar_string(AWAR_DBB_HISTORY, "", aw_def); // '\n'-separated string containing visited nodes
196 
197  aw_root->awar_int(AWAR_DBB_RECSIZE, 0, aw_def)->add_callback(nodedisplay_changed_cb); // collect size recursive?
198 
199  // hex-dump-options
200  aw_root->awar_int(AWAR_DUMP_HEX, 1, aw_def)->add_callback(nodedisplay_changed_cb); // show hex ?
201  aw_root->awar_int(AWAR_DUMP_ASCII, 1, aw_def)->add_callback(nodedisplay_changed_cb); // show ascii ?
202  aw_root->awar_int(AWAR_DUMP_SPACE, 1, aw_def)->add_callback(nodedisplay_changed_cb); // space bytes
203  aw_root->awar_int(AWAR_DUMP_WIDTH, 16, aw_def)->add_callback(nodedisplay_changed_cb); // bytes/line
204  aw_root->awar_int(AWAR_DUMP_BLOCK, 8, aw_def)->add_callback(nodedisplay_changed_cb); // separate each bytes
205 }
206 
207 static GBDATA *GB_search_numbered(GBDATA *gbd, const char *str, GB_TYPES create) { // @@@ this may be moved to ARBDB-sources
209  if (str) {
210  if (str[0] == '/' && str[1] == 0) { // root
211  return GB_get_root(gbd);
212  }
213 
214  const char *first_bracket = strchr(str, '[');
215  if (first_bracket) {
216  const char *second_bracket = strchr(first_bracket+1, ']');
217  if (second_bracket && (second_bracket[1] == 0 || second_bracket[1] == '/')) {
218  int count = atoi(first_bracket+1);
219  if (count >= 0 && isdigit(first_bracket[1])) {
220  // we are sure we have sth with number in brackets (e.g. "/species_data/species[42]/name")
221  const char *previous_slash = first_bracket-1;
222  while (previous_slash >= str && previous_slash[0] != '/') previous_slash--; //
223  if (previous_slash<str) previous_slash = NULp;
224 
225  GBDATA *gb_parent = NULp;
226  {
227  if (previous_slash) { // found a slash
228  char *parent_path = ARB_strpartdup(str, previous_slash-1);
229 
230  // we are sure parent path does not contain brackets -> search normal
231  if (parent_path[0] == 0) { // that means : root-item is numbered (e.g. '/species_data[7]/...')
232  gb_parent = GB_get_root(gbd);
233  }
234  else {
235  gb_parent = GB_search(gbd, parent_path, GB_FIND);
236  }
237 
238  if (!gb_parent) fprintf(stderr, "Warning: parent '%s' not found\n", parent_path);
239  free(parent_path);
240  }
241  else {
242  gb_parent = gbd;
243  }
244  }
245 
246  if (gb_parent) {
247  GBDATA *gb_son = NULp;
248  {
249  const char *name_start = previous_slash ? previous_slash+1 : str;
250  char *key_name = ARB_strpartdup(name_start, first_bracket-1);
251  int c = 0;
252 
253  gb_son = GB_entry(gb_parent, key_name);
254  while (c<count && gb_son) {
255  gb_son = GB_nextEntry(gb_son);
256  if (gb_son) ++c;
257  }
258 
259  if (!gb_son) fprintf(stderr, "Warning: did not find %i. son '%s'\n", count, key_name);
260  free(key_name);
261  }
262 
263  if (gb_son) {
264  const char *rest = NULp;
265  if (second_bracket[1] == '/') { // continue search ?
266  if (second_bracket[2]) {
267  rest = second_bracket+2;
268  }
269  }
270 
271  return rest
272  ? GB_search_numbered(gb_son, rest, create) // recursive search
273  : gb_son; // numbering occurred at end of search path
274  }
275  }
276  else {
277  fprintf(stderr, "Warning: don't know where to start numbered search in '%s'\n", str);
278  }
279 
280  return NULp; // not found
281  }
282  else {
283  fprintf(stderr, "Warning: Illegal content in search path - expected digits at '%s'\n", first_bracket+1);
284  }
285  }
286  else {
287  fprintf(stderr, "Warning: Unbalanced or illegal [] in search path (%s)\n", str);
288  }
289  }
290  // no brackets -> normal search
291  }
293  return GB_search(gbd, str, create); // do normal search
294 }
295 
296 // -----------------------
297 // class KnownDB
298 
299 class KnownDB {
301 
302  string description;
303  string current_path;
304 
305 public:
306  KnownDB(GBDATA *gb_main_, const char *description_) :
307  gb_main(gb_main_),
308  description(description_),
309  current_path("/")
310  {}
311 
312  const GBDATA *get_db() const { return gb_main; }
313  const string& get_description() const { return description; }
314 
315  const string& get_path() const { return current_path; }
316  void set_path(const string& path) { current_path = path; }
317  void set_path(const char* path) { current_path = path; }
318 };
319 
320 class hasDB {
321  GBDATA *db;
322 public:
323  explicit hasDB(GBDATA *gbm) : db(gbm) {}
324  bool operator()(const KnownDB& kdb) { return kdb.get_db() == db; }
325 };
326 
327 // --------------------------
328 // class DB_browser
329 
330 class DB_browser;
331 static DB_browser *get_the_browser(bool autocreate);
332 
333 class DB_browser : virtual Noncopyable {
334  typedef vector<KnownDB>::iterator KnownDBiterator;
335 
336  vector<KnownDB> known_databases;
337  size_t current_db; // index of current db (in known_databases)
338 
339  AW_window *aww; // browser window
340  AW_option_menu_struct *oms; // the DB selector
341  AW_selection_list *browse_list; // the browse subwindow
342 
343  static SmartPtr<DB_browser> the_browser;
344  friend DB_browser *get_the_browser(bool autocreate);
345 
346  void update_DB_selector();
347 
348 public:
349  DB_browser() : current_db(0), aww(NULp), oms(NULp) {}
350 
351  void add_db(GBDATA *gb_main, const char *description) {
352  known_databases.push_back(KnownDB(gb_main, description));
353  if (aww) update_DB_selector();
354  }
355 
356  void del_db(GBDATA *gb_main) {
357  KnownDBiterator known = find_if(known_databases.begin(), known_databases.end(), hasDB(gb_main));
358  if (known != known_databases.end()) known_databases.erase(known);
359 #if defined(DEBUG)
360  else arb_assert(0); // no need to delete unknown databases
361 #endif // DEBUG
362 
363  if (aww) update_DB_selector();
364  }
365 
366  AW_window *get_window(AW_root *aw_root);
367  AW_selection_list *get_browser_list() { return browse_list; }
368 
369  bool legal_selected_db() const { return current_db < known_databases.size(); }
370 
371  size_t get_selected_db() const { arb_assert(legal_selected_db()); return current_db; }
372  void set_selected_db(size_t idx) { arb_assert(idx < known_databases.size()); current_db = idx; }
373 
374  const char *get_path() const { return known_databases[get_selected_db()].get_path().c_str(); }
375  void set_path(const char *new_path) { known_databases[get_selected_db()].set_path(new_path); }
376 
377  GBDATA *get_db() const {
378  return legal_selected_db() ? const_cast<GBDATA*>(known_databases[get_selected_db()].get_db()) : NULp;
379  }
380 };
381 
382 
383 // -----------------------------
384 // DB_browser singleton
385 
386 SmartPtr<DB_browser> DB_browser::the_browser;
387 
388 static DB_browser *get_the_browser(bool autocreate = true) {
389  if (DB_browser::the_browser.isNull() && autocreate) {
390  DB_browser::the_browser = new DB_browser;
391  }
392  return &*DB_browser::the_browser;
393 }
394 
395 // --------------------------
396 // announce databases
397 
398 static void browser_auto_forget_db(AW_root*, GBDATA *gb_main) {
399  DB_browser *browser = get_the_browser(false);
400  arb_assert(browser);
401  arb_assert(gb_main);
402  if (browser) browser->del_db(gb_main);
403 }
404 
405 void AWT_announce_db_to_browser(GBDATA *gb_main, const char *description) {
406  get_the_browser()->add_db(gb_main, description);
407  ARB_atdisconnect_callback(makeArbDisconnectCallback(browser_auto_forget_db));
408 }
409 
410 static void AWT_announce_properties_to_browser(GBDATA *gb_defaults, const char *defaults_name) {
411  AWT_announce_db_to_browser(gb_defaults, GBS_global_string("Properties (%s)", defaults_name));
412 }
413 
414 // ---------------------------------------
415 // browser window callbacks
416 
417 static void toggle_tmp_cb(AW_window *aww) {
418  AW_awar *awar_path = aww->get_root()->awar(AWAR_DBB_PATH);
419  char *path = awar_path->read_string();
420  bool done = false;
421 
422  if (ARB_strBeginsWith(path, "/tmp")) {
423  if (path[4] == '/') {
424  awar_path->write_string(path+4);
425  done = true;
426  }
427  else if (path[4] == 0) {
428  awar_path->write_string("/");
429  done = true;
430  }
431  }
432 
433  if (!done && !is_dbbrowser_pseudo_path(path)) {
434  char *path_in_tmp = GBS_global_string_copy("/tmp%s", path);
435 
436  char *lslash = strrchr(path_in_tmp, '/');
437  if (lslash && !lslash[1]) { // ends with '/'
438  lslash[0] = 0; // cut-off trailing '/'
439  }
440  awar_path->write_string(path_in_tmp);
441  free(path_in_tmp);
442  }
443  free(path);
444 }
445 
446 static void show_history_cb(AW_window *aww) {
447  aww->get_root()->awar(AWAR_DBB_PATH)->write_string(HISTORY_PSEUDO_PATH);
448 }
449 
450 static void goto_root_cb(AW_window *aww) {
451  AW_awar *awar_path = aww->get_root()->awar(AWAR_DBB_PATH);
452  awar_path->write_string("/");
453 }
454 static void go_up_cb(AW_window *aww) {
455  AW_awar *awar_path = aww->get_root()->awar(AWAR_DBB_PATH);
456  char *path = awar_path->read_string();
457  char *lslash = strrchr(path, '/');
458 
459  if (lslash) {
460  lslash[0] = 0;
461  if (!path[0]) strcpy(path, "/");
462  awar_path->write_string(path);
463  }
464 }
465 
466 // --------------------------
467 // browser commands:
468 
469 #define BROWSE_CMD_PREFIX "browse_cmd___"
470 #define BROWSE_CMD_GOTO_VALID_NODE BROWSE_CMD_PREFIX "goto_valid_node"
471 #define BROWSE_CMD_GO_UP BROWSE_CMD_PREFIX "go_up"
472 
473 struct BrowserCommand {
474  const char *name;
475  GB_ERROR (*function)(AW_window *);
476 };
477 
478 
479 static GB_ERROR browse_cmd_go_up(AW_window *aww) {
480  go_up_cb(aww);
481  return NULp;
482 }
483 static GB_ERROR browse_cmd_goto_valid_node(AW_window *aww) {
484  AW_root *aw_root = aww->get_root();
485  AW_awar *awar_path = aw_root->awar(AWAR_DBB_PATH);
486  char *path = awar_path->read_string();
487  int len = strlen(path);
488  GBDATA *gb_main = get_the_browser()->get_db();
489 
490  if (!gb_main) {
491  aw_message("previously selected database vanished => selected first database");
492  aw_root->awar(AWAR_DBB_DB)->rewrite_int(0);
493  }
494  else {
495  GB_transaction t(gb_main);
496  while (len>0 && !GB_search_numbered(gb_main, path, GB_FIND)) {
497  GB_clear_error();
498  path[--len] = 0;
499  }
500  }
501 
502  awar_path->write_string(len ? path : "/");
503  aw_root->awar(AWAR_DBB_BROWSE)->write_string("");
504  return NULp;
505 }
506 
507 static BrowserCommand browser_command_table[] = {
508  { BROWSE_CMD_GOTO_VALID_NODE, browse_cmd_goto_valid_node },
509  { BROWSE_CMD_GO_UP, browse_cmd_go_up },
510  { NULp, NULp }
511 };
512 
513 static void execute_DB_browser_command(AW_window *aww, const char *command) {
514  int idx;
515  for (idx = 0; browser_command_table[idx].name; ++idx) {
516  if (strcmp(command, browser_command_table[idx].name) == 0) {
517  GB_ERROR error = browser_command_table[idx].function(aww);
518  if (error) aw_message(error);
519  break;
520  }
521  }
522 
523  if (!browser_command_table[idx].name) {
524  aw_message(GBS_global_string("Unknown DB-browser command '%s'", command));
525  }
526 }
527 
528 // ----------------------------
529 // the browser window
530 
531 static AW_window *create_db_browser(AW_root *aw_root) {
532  return get_the_browser()->get_window(aw_root);
533 }
534 
535 struct counterPair {
536  int occur, count;
537  counterPair() : occur(0), count(0) {}
538 };
539 
540 inline void insert_history_selection(AW_selection_list *sel, const char *entry, int wanted_db) {
541  const char *colon = strchr(entry, ':');
542  if (colon && (atoi(entry) == wanted_db)) {
543  sel->insert(colon+1, colon+1);
544  }
545 }
546 
547 
548 static char *get_container_description(GBDATA *gbd) {
549  char *content = NULp;
550  const char *known_children[] = { "@name", "name", "key_name", "alignment_name", "group_name", "key_text", NULp };
551  for (int i = 0; known_children[i]; ++i) {
552  GBDATA *gb_known = GB_entry(gbd, known_children[i]);
553  if (gb_known && GB_read_type(gb_known) != GB_DB && !GB_nextEntry(gb_known)) { // exactly one child exits
554  char *asStr = GB_read_as_string(gb_known);
555  content = GBS_global_string_copy("[%s=%s]", known_children[i], asStr);
556  free(asStr);
557  break;
558  }
559  }
560 
561  return content;
562 }
563 
564 static char *get_dbentry_content(GBDATA *gbd, GB_TYPES type, bool shorten_repeats, const MemDump& dump) {
565  arb_assert(type != GB_DB);
566 
567  char *content = NULp;
568  if (!dump.wrapped()) content = GB_read_as_string(gbd);
569  if (!content) { // use dumper
570  long size;
571 
572  switch (type) {
573  case GB_BIT:
574  case GB_BYTE: size = 1; break;
575  case GB_INT: size = sizeof(int32_t); break;
576  case GB_FLOAT: size = sizeof(float); break;
577  case GB_POINTER: size = sizeof(GBDATA*); break;
578 
579  case GB_BITS:
580  case GB_BYTES:
581  case GB_INTS:
582  case GB_FLOATS:
583  case GB_STRING: size = GB_read_count(gbd); break;
584 
585  default: aw_assert(0); break;
586  }
587 
588  const int plen = 30;
589  GBS_strstruct buf(dump.mem_needed_for_dump(size)+plen);
590 
591  if (!dump.wrapped()) buf.nprintf(plen, "<%li bytes>: ", size);
592  dump.dump_to(buf, GB_read_pntr(gbd), size);
593 
594  content = buf.release();
595  shorten_repeats = false;
596  }
597  arb_assert(content);
598 
599  size_t len = shorten_repeats ? GBS_shorten_repeated_data(content) : strlen(content);
600  if (!dump.wrapped() && len>(ENTRY_MAX_LENGTH+15)) {
601  content[ENTRY_MAX_LENGTH] = 0;
602  freeset(content, GBS_global_string_copy("%s [rest skipped]", content));
603  }
604  return content;
605 }
606 
607 static void update_browser_selection_list(AW_root *aw_root, AW_selection_list *id) {
609  DB_browser *browser = get_the_browser();
610  char *path = aw_root->awar(AWAR_DBB_PATH)->read_string();
611  bool is_root;
612  GBDATA *node = NULp;
613 
614  {
615  GBDATA *gb_main = browser->get_db();
616  if (gb_main) {
617  GB_transaction ta(gb_main);
618  is_root = (strcmp(path, "/") == 0);
619  node = GB_search_numbered(gb_main, path, GB_FIND);
620  }
621  }
622 
623  id->clear();
624 
625  if (!node) {
626  if (strcmp(path, HISTORY_PSEUDO_PATH) == 0) {
627  GB_clear_error(); // ignore error about invalid key
628  char *history = aw_root->awar(AWAR_DBB_HISTORY)->read_string();
629  id->insert("Previously visited nodes:", "");
630  char *start = history;
631  int curr_db = aw_root->awar(AWAR_DBB_DB)->read_int();
632 
633  for (char *lf = strchr(start, '\n'); lf; start = lf+1, lf = strchr(start, '\n')) {
634  lf[0] = 0;
635  insert_history_selection(id, start, curr_db);
636  }
637  insert_history_selection(id, start, curr_db);
638  free(history);
639  }
640  else {
641  if (GB_have_error()) id->insert(GBS_global_string("Error: %s", GB_await_error()), "");
642  id->insert("No such node!", "");
643  id->insert("-> goto valid node", BROWSE_CMD_GOTO_VALID_NODE);
644  }
645  }
646  else {
647  map<string, counterPair> child_count;
648  GB_transaction ta(browser->get_db());
649 
650  for (GBDATA *child = GB_child(node); child; child = GB_nextChild(child)) {
651  const char *key_name = GB_read_key_pntr(child);
652  child_count[key_name].occur++;
653  }
654 
655  int maxkeylen = 0;
656  int maxtypelen = 0;
657  for (map<string, counterPair>::iterator i = child_count.begin(); i != child_count.end(); ++i) {
658  {
659  int keylen = i->first.length();
660  int maxcount = i->second.occur;
661 
662  if (maxcount != 1) {
663  keylen += 2; // brackets
664  while (maxcount) { maxcount /= 10; keylen++; } // increase keylen for each digit
665  }
666 
667  if (keylen>maxkeylen) maxkeylen = keylen;
668  }
669 
670  {
671  GBDATA *child = GB_entry(node, i->first.c_str()); // find first child
672  const char *type_name = GB_get_type_name(child);
673  int typelen = strlen(type_name);
674 
675  if (typelen>maxtypelen) maxtypelen = typelen;
676  }
677  }
678 
679  if (!is_root) {
680  id->insert(GBS_global_string("%-*s parent container", maxkeylen, ".."), BROWSE_CMD_GO_UP);
681  id->insert(GBS_global_string("%-*s container", maxkeylen, "."), "");
682  }
683  else {
684  id->insert(GBS_global_string("%-*s root container", maxkeylen, "/"), "");
685  }
686 
687  // collect children and sort them
688 
689  vector<list_entry> sorted_children;
690 
691  MemDump simpleDump(false, true, false);
692  for (GBDATA *child = GB_child(node); child; child = GB_nextChild(child)) {
693  list_entry entry;
694  entry.key_name = GB_read_key_pntr(child);
695  entry.childCount = -1;
696  entry.type = GB_read_type(child);
697  entry.gbd = child;
698 
699  int occurrences = child_count[entry.key_name].occur;
700  if (occurrences != 1) {
701  entry.childCount = child_count[entry.key_name].count;
702  child_count[entry.key_name].count++;
703  }
704  char *display = NULp;
705  if (entry.type == GB_DB) {
706  display = get_container_description(entry.gbd);
707  }
708  else {
709  display = get_dbentry_content(entry.gbd, entry.type, true, simpleDump);
710  }
711  if (display) entry.content = display;
712  sorted_children.push_back(entry);
713  }
714 
715  list_entry::sort_order = (SortOrder)aw_root->awar(AWAR_DBB_ORDER)->read_int();
716  if (list_entry::sort_order != SORT_NONE) {
717  sort(sorted_children.begin(), sorted_children.end());
718  }
719 
720  for (vector<list_entry>::iterator ch = sorted_children.begin(); ch != sorted_children.end(); ++ch) {
721  const list_entry& entry = *ch;
722  const char *key_name = entry.key_name;
723  char *numbered_keyname = NULp;
724 
725  if (entry.childCount >= 0) {
726  numbered_keyname = GBS_global_string_copy("%s[%i]", key_name, entry.childCount);
727  }
728 
729  key_name = numbered_keyname ? numbered_keyname : key_name;
730  const char *displayed = GBS_global_string("%-*s %-*s %s", maxkeylen, key_name, maxtypelen, GB_get_type_name(entry.gbd), entry.content.c_str());
731  id->insert(displayed, key_name);
732 
733  free(numbered_keyname);
734  }
735  }
736  id->insert_default("", "");
737  id->update();
738 
739  free(path);
741 }
742 
743 static void order_changed_cb(AW_root *aw_root) {
744  DB_browser *browser = get_the_browser();
745  update_browser_selection_list(aw_root, browser->get_browser_list());
746 }
747 
748 inline char *strmove(char *dest, char *source) {
749  return (char*)memmove(dest, source, strlen(source)+1);
750 }
751 
752 static void add_to_history(AW_root *aw_root, const char *path) {
753  // adds 'path' to history
754 
755  if (strcmp(path, HISTORY_PSEUDO_PATH) != 0) {
756  int db = aw_root->awar(AWAR_DBB_DB)->read_int();
757  AW_awar *awar_history = aw_root->awar(AWAR_DBB_HISTORY);
758  char *old_history = awar_history->read_string();
759  char *entry = GBS_global_string_copy("%i:%s", db, path);
760  int entry_len = strlen(entry);
761 
762  char *found = strstr(old_history, entry);
763  while (found) {
764  if (found == old_history || found[-1] == '\n') { // found start of an entry
765  if (found[entry_len] == '\n') {
766  strmove(found, found+entry_len+1);
767  found = strstr(old_history, entry);
768  }
769  else if (found[entry_len] == 0) { // found as last entry
770  if (found == old_history) { // which is also first entry
771  old_history[0] = 0; // empty history
772  }
773  else {
774  strmove(found, found+entry_len+1);
775  }
776  found = strstr(old_history, entry);
777  }
778  else {
779  found = strstr(found+1, entry);
780  }
781  }
782  else {
783  found = strstr(found+1, entry);
784  }
785  }
786 
787  if (old_history[0]) {
788  char *new_history = GBS_global_string_copy("%s\n%s", entry, old_history);
789 
790  while (strlen(old_history)>HISTORY_MAX_LENGTH) { // shorten history
791  char *llf = strrchr(old_history, '\n');
792  if (!llf) break;
793  llf[0] = 0;
794  }
795 
796  awar_history->write_string(new_history);
797  free(new_history);
798  }
799  else {
800  awar_history->write_string(entry);
801  }
802 
803  free(entry);
804  free(old_history);
805  }
806 }
807 
808 static bool inside_path_change = false;
809 static GBDATA *gb_tracked_node = NULp;
810 
811 static void selected_node_modified_cb(GBDATA *gb_node, GB_CB_TYPE cb_type) {
812  arb_assert(gb_node == gb_tracked_node);
813 
814  if (cb_type & GB_CB_DELETE) {
815  gb_tracked_node = NULp; // no need to remove callback ?
816  }
817 
818  if (!inside_path_change) { // ignore refresh callbacks triggered by dbbrowser-awars
819  static bool avoid_recursion = false;
820  if (!avoid_recursion) {
821  LocallyModify<bool> flag(avoid_recursion, true);
823 
824  AW_root *aw_root = AW_root::SINGLETON;
825  AW_awar *awar_path = aw_root->awar_no_error(AWAR_DBB_PATH);
826  if (awar_path) { // avoid crash during exit
827  AW_awar *awar_child = aw_root->awar(AWAR_DBB_BROWSE);
828  const char *child = awar_child->read_char_pntr();
829 
830  if (child[0]) {
831  const char *path = awar_path->read_char_pntr();
832  const char *new_path;
833 
834  if (!path[0] || !path[1]) {
835  new_path = GBS_global_string("/%s", child);
836  }
837  else {
838  new_path = GBS_global_string("%s/%s", path, child);
839  }
840 
841  char *fixed_path = GBS_string_eval(new_path, "//=/");
842  awar_path->write_string(fixed_path);
843  free(fixed_path);
844  }
845  else {
846  awar_path->touch();
847  }
848  }
849  GBS_restore_global_buffers(old_buffers);
850  }
851  }
852 }
853 static void untrack_node() {
854  if (gb_tracked_node) {
855  GB_remove_callback(gb_tracked_node, GB_CB_ALL, makeDatabaseCallback(selected_node_modified_cb));
856  gb_tracked_node = NULp;
857  }
858 }
859 static void track_node(GBDATA *gb_node) {
860  untrack_node();
861  GB_ERROR error = GB_add_callback(gb_node, GB_CB_ALL, makeDatabaseCallback(selected_node_modified_cb));
862  if (error) {
863  aw_message(error);
864  }
865  else {
866  gb_tracked_node = gb_node;
867  }
868 }
869 
870 static string get_node_info_string(AW_root *aw_root, GBDATA *gb_selected_node, const char *fullpath) {
871  string info;
872  info += GBS_global_string("Fullpath | '%s'\n", fullpath);
873 
874  if (!gb_selected_node) {
875  info += "Address | NULp\n";
876  info += GBS_global_string("Error | %s\n", GB_have_error() ? GB_await_error() : "<none>");
877  }
878  else {
879  add_to_history(aw_root, fullpath);
880 
881  info += GBS_global_string("Address | %p\n", gb_selected_node);
882  info += GBS_global_string("Key index | %i\n", GB_get_quark(gb_selected_node));
883 
884 
885  GB_SizeInfo sizeInfo;
886  bool collect_recursive = aw_root->awar(AWAR_DBB_RECSIZE)->read_int();
887 
888  GB_TYPES type = GB_read_type(gb_selected_node);
889  if (collect_recursive || type != GB_DB) {
890  sizeInfo.collect(gb_selected_node);
891  }
892 
893  string structure_add;
894  string childs;
895 
896  if (type == GB_DB) {
897  long struct_size = GB_calc_structure_size(gb_selected_node);
898  if (collect_recursive) {
899  structure_add = GBS_global_string(" (this: %s)", GBS_readable_size(struct_size, "b"));
900  }
901  else {
902  sizeInfo.structure = struct_size;
903  }
904 
905  info += "Node type | container\n";
906 
907  childs = GBS_global_string("Childs | %li", GB_number_of_subentries(gb_selected_node));
908  if (collect_recursive) {
909  childs = childs+" (rec: " + GBS_readable_size(sizeInfo.containers, "containers");
910  childs = childs+" + " + GBS_readable_size(sizeInfo.terminals, "terminals");
911  childs = childs+" = " + GBS_readable_size(sizeInfo.terminals+sizeInfo.containers, "nodes")+')';
912  }
913  childs += '\n';
914 
915  {
916  LocallyModify<bool> flag2(inside_path_change, true);
917  aw_root->awar(AWAR_DBB_BROWSE)->write_string("");
918  aw_root->awar(AWAR_DBB_PATH)->write_string(fullpath);
919  }
920  }
921  else {
922  info += GBS_global_string("Node type | data [type=%s]\n", GB_get_type_name(gb_selected_node));
923  }
924 
925  long overall = sizeInfo.mem+sizeInfo.structure;
926 
927  info += GBS_global_string("Data size | %7s\n", GBS_readable_size(sizeInfo.data, "b"));
928  info += GBS_global_string("Memory | %7s %5.2f%%\n", GBS_readable_size(sizeInfo.mem, "b"), double(sizeInfo.mem)/overall*100+.0049);
929  info += GBS_global_string("Structure | %7s %5.2f%%", GBS_readable_size(sizeInfo.structure, "b"), double(sizeInfo.structure)/overall*100+.0049) + structure_add + '\n';
930  info += GBS_global_string("Overall | %7s\n", GBS_readable_size(overall, "b"));
931  if (sizeInfo.data) {
932  info += GBS_global_string("CompRatio | %5.2f%% (mem-only: %5.2f%%)\n", double(overall)/sizeInfo.data*100+.0049, double(sizeInfo.mem)/sizeInfo.data*100+.0049);
933  }
934  info += childs;
935 
936  {
937  bool is_tmp = GB_is_temporary(gb_selected_node);
938  bool not_tmp_but_in_tmp = !is_tmp && GB_in_temporary_branch(gb_selected_node);
939 
940  if (is_tmp || not_tmp_but_in_tmp) {
941  info += GBS_global_string("Temporary | yes%s\n", not_tmp_but_in_tmp ? " (in temporary branch)" : "");
942  }
943  }
944 
945  GBDATA *gb_recurse_here = NULp;
946  char *recursed_path = NULp;
947  {
948  AW_awar *awar_this = aw_root->awar_no_error(fullpath+1); // skip leading slash
949  if (awar_this) {
950  bool is_mapped = awar_this->is_mapped();
951  info += "AWAR | yes\n";
952  if (is_mapped) {
953  gb_recurse_here = awar_this->gb_var;
954  recursed_path = ARB_strdup(GB_get_db_path(gb_recurse_here));
955  info = info + " | mapped to '" + recursed_path + "' (see below)\n";
956  }
957  }
958  }
959 
960  info += GBS_global_string("Security | read=%i write=%i delete=%i\n",
961  GB_read_security_read(gb_selected_node),
962  GB_read_security_write(gb_selected_node),
963  GB_read_security_delete(gb_selected_node));
964 
965  char *callback_info = GB_get_callback_info(gb_selected_node);
966  if (callback_info) {
968  GBT_splitNdestroy_string(callbacks, callback_info, "\n", SPLIT_DROPEMPTY);
969 
970  for (size_t i = 0; i<callbacks.size(); ++i) {
971  const char *prefix = i ? " " : "Callbacks";
972  info = info + prefix + " | " + callbacks[i] + '\n';
973  }
974 
975  if (gb_selected_node == gb_tracked_node) {
976  info += " | (Note: one callback was installed by this browser)\n";
977  }
978  }
979 
980  if (type != GB_DB) {
981  MemDump dump = make_userdefined_MemDump(aw_root);
982  char *content = get_dbentry_content(gb_selected_node, GB_read_type(gb_selected_node), false, dump);
983  if (content[0]) {
984  info = info+"\nContent:\n"+content+'\n';
985  }
986  else {
987  info += "\nNo content.\n";
988  }
989  free(content);
990  }
991 
992  if (gb_recurse_here) {
993  GBDATA *gb_sel_main = GB_get_root(gb_selected_node);
994  GBDATA *gb_sub_main = GB_get_root(gb_recurse_here);
995  const bool same_DB = gb_sel_main == gb_sub_main;
996 
997  GB_transaction ta(gb_sub_main); // target database may differ from current
998 
999  string subinfo = get_node_info_string(aw_root, gb_recurse_here, recursed_path);
1000 
1001  info +=
1002  "\n"
1003  + string(same_DB
1004  ?
1005  "===========================\n"
1006  "Recursive database element:\n"
1007  :
1008  "===================================================\n"
1009  "Recursive database element (in different database):\n"
1010  )
1011  + "\n"
1012  + subinfo;
1013 
1014  freenull(recursed_path);
1015  }
1016  }
1017  return info;
1018 }
1019 
1020 static void child_changed_cb(AW_root *aw_root) {
1021  char *child = aw_root->awar(AWAR_DBB_BROWSE)->read_string();
1022  if (strncmp(child, BROWSE_CMD_PREFIX, sizeof(BROWSE_CMD_PREFIX)-1) == 0) { // a symbolic browser command
1023  execute_DB_browser_command(get_the_browser()->get_window(aw_root), child);
1024  }
1025  else {
1026  char *path = aw_root->awar(AWAR_DBB_PATH)->read_string();
1027 
1028  if (strcmp(path, HISTORY_PSEUDO_PATH) == 0) {
1029  if (child[0]) {
1030  LocallyModify<bool> flag(inside_path_change, true);
1031  aw_root->awar(AWAR_DBB_PATH)->write_string(child);
1032  }
1033  }
1034  else {
1035  static bool avoid_recursion = false;
1036  if (!avoid_recursion) {
1037  LocallyModify<bool> flag(avoid_recursion, true);
1038 
1039  char *fullpath;
1040  if (strcmp(path, "/") == 0) {
1041  fullpath = GBS_global_string_copy("/%s", child);
1042  }
1043  else if (child[0] == 0) {
1044  fullpath = ARB_strdup(path);
1045  }
1046  else {
1047  fullpath = GBS_global_string_copy("%s/%s", path, child);
1048  }
1049 
1050  DB_browser *browser = get_the_browser();
1051  GBDATA *gb_main = browser->get_db();
1052 
1053  if (gb_main) {
1054  GB_transaction ta(gb_main);
1055  GBDATA *gb_selected_node = GB_search_numbered(gb_main, fullpath, GB_FIND);
1056 
1057  string info = get_node_info_string(aw_root, gb_selected_node, fullpath);
1058 
1059 
1060  aw_root->awar(AWAR_DBB_INFO)->write_string(info.c_str());
1061  }
1062  free(fullpath);
1063  }
1064  }
1065 
1066  free(path);
1067  }
1068  free(child);
1069 }
1070 
1071 static void path_changed_cb(AW_root *aw_root) {
1072  static bool avoid_recursion = false;
1073  if (!avoid_recursion) {
1075  LocallyModify<bool> flag(avoid_recursion, true);
1076 
1077  DB_browser *browser = get_the_browser();
1078  char *goto_child = NULp;
1079 
1080  GBDATA *gb_main = browser->get_db();
1081  if (gb_main) {
1082  GB_transaction t(gb_main);
1083  AW_awar *awar_path = aw_root->awar(AWAR_DBB_PATH);
1084  char *path = awar_path->read_string();
1085  GBDATA *found = GB_search_numbered(gb_main, path, GB_FIND);
1086 
1087  if (found && GB_read_type(found) != GB_DB) { // exists, but is not a container
1088  char *lslash = strrchr(path, '/');
1089  if (lslash) {
1090  goto_child = ARB_strdup(lslash+1);
1091  lslash[lslash == path] = 0; // truncate at last slash (but keep sole slash)
1092  awar_path->write_string(path);
1093  }
1094  }
1095 
1096  if (found) {
1097  LocallyModify<bool> flag2(inside_path_change, true);
1098  add_to_history(aw_root, goto_child ? GBS_global_string("%s/%s", path, goto_child) : path);
1099  GBDATA *father = GB_get_father(found);
1100  track_node(father ? father : found);
1101  }
1102  else if (is_dbbrowser_pseudo_path(path)) {
1103  GB_clear_error(); // ignore error about invalid key
1104  }
1105  else if (GB_have_error()) {
1107  }
1108  browser->set_path(path);
1109  free(path);
1110  }
1111 
1112  update_browser_selection_list(aw_root, browser->get_browser_list());
1113 
1114  LocallyModify<bool> flag2(inside_path_change, true);
1115  aw_root->awar(AWAR_DBB_BROWSE)->rewrite_string(null2empty(goto_child));
1117  }
1118 }
1119 static void db_changed_cb(AW_root *aw_root) {
1120  int selected = aw_root->awar(AWAR_DBB_DB)->read_int();
1121  DB_browser *browser = get_the_browser();
1122 
1123  LocallyModify<bool> flag(inside_path_change, true);
1124  browser->set_selected_db(selected);
1125  aw_root->awar(AWAR_DBB_PATH)->rewrite_string(browser->get_path());
1126 }
1127 
1128 void DB_browser::update_DB_selector() {
1129  if (!oms) oms = aww->create_option_menu(AWAR_DBB_DB);
1130  else aww->clear_option_menu(oms);
1131 
1132  int idx = 0;
1133  for (KnownDBiterator i = known_databases.begin(); i != known_databases.end(); ++i, ++idx) {
1134  const KnownDB& db = *i;
1135  aww->insert_option(db.get_description().c_str(), "", idx);
1136  }
1137  aww->update_option_menu();
1138 }
1139 
1140 AW_window *DB_browser::get_window(AW_root *aw_root) {
1141  arb_assert(!known_databases.empty()); // have no db to browse
1142 
1143  if (!aww) {
1144  // select current db+path
1145  {
1146  int wanted_db = aw_root->awar(AWAR_DBB_DB)->read_int();
1147  int known = known_databases.size();
1148  if (wanted_db >= known) {
1149  wanted_db = 0;
1150  aw_root->awar(AWAR_DBB_DB)->write_int(wanted_db);
1151  aw_root->awar(AWAR_DBB_PATH)->write_string("/"); // reset
1152  }
1153  set_selected_db(wanted_db);
1154 
1155  char *wanted_path = aw_root->awar(AWAR_DBB_PATH)->read_string();
1156  known_databases[wanted_db].set_path(wanted_path);
1157  free(wanted_path);
1158  }
1159 
1160  AW_window_simple *aws = new AW_window_simple;
1161  aww = aws;
1162  aws->init(aw_root, "DB_BROWSER", "ARB database browser");
1163  aws->load_xfig("dbbrowser.fig");
1164 
1165  aws->at("close"); aws->callback(AW_POPDOWN);
1166  aws->create_button("CLOSE", "CLOSE", "C");
1167 
1168  aws->at("db");
1169  update_DB_selector();
1170 
1171  aws->at("order");
1172  aws->create_option_menu(AWAR_DBB_ORDER);
1173  for (int idx = 0; idx<SORT_COUNT; ++idx) {
1174  aws->insert_option(sort_order_name[idx], "", idx);
1175  }
1176  aws->update_option_menu();
1177 
1178  aws->at("path");
1179  aws->create_input_field(AWAR_DBB_PATH, 10);
1180 
1181  aws->auto_space(10, 10);
1182  aws->button_length(8);
1183 
1184  aws->at("navigation");
1185  aws->callback(go_up_cb); aws->create_button("go_up", "Up");
1186  aws->callback(goto_root_cb); aws->create_button("goto_root", "Top");
1187  aws->callback(show_history_cb); aws->create_button("history", "History");
1188  aws->callback(toggle_tmp_cb); aws->create_button("toggle_tmp", "/tmp");
1189 
1190  aws->label("Rec.size"); aws->create_toggle(AWAR_DBB_RECSIZE);
1191 
1192  aws->at("browse");
1193  browse_list = aws->create_selection_list(AWAR_DBB_BROWSE);
1194  update_browser_selection_list(aw_root, browse_list);
1195 
1196  aws->at("infoopt");
1197  aws->label("ASCII"); aws->create_toggle (AWAR_DUMP_ASCII);
1198  aws->label("Hex"); aws->create_toggle (AWAR_DUMP_HEX);
1199  aws->label("Sep?"); aws->create_toggle (AWAR_DUMP_SPACE);
1200  aws->label("Width"); aws->create_input_field(AWAR_DUMP_WIDTH, 3);
1201  aws->label("Block"); aws->create_input_field(AWAR_DUMP_BLOCK, 3);
1202 
1203  aws->at("info");
1204  aws->create_text_field(AWAR_DBB_INFO, 40, 40);
1205 
1206  // add callbacks
1207  aw_root->awar(AWAR_DBB_BROWSE)->add_callback(child_changed_cb);
1208  aw_root->awar(AWAR_DBB_PATH)->add_callback(path_changed_cb);
1209  aw_root->awar(AWAR_DBB_DB)->add_callback(db_changed_cb);
1210  aw_root->awar(AWAR_DBB_ORDER)->add_callback(order_changed_cb);
1211 
1212  db_changed_cb(aw_root); // force update
1213  }
1214  return aww;
1215 }
1216 
1217 static void callallcallbacks(AW_window *aww, int mode) {
1218  static bool running = false; // avoid deadlock
1219  if (!running) {
1220  LocallyModify<bool> flag(running, true);
1221  aww->get_root()->callallcallbacks(mode);
1222  }
1223 }
1224 
1225 
1226 
1227 void AWT_create_debug_menu(AW_window *awmm) {
1228  awmm->create_menu("4debugz", "z", AWM_ALL);
1229 
1230  awmm->insert_menu_topic(awmm->local_id("-db_browser"), "Browse loaded database(s)", "B", NULp, AWM_ALL, create_db_browser);
1231 
1232  awmm->sep______________();
1233  {
1234  awmm->insert_sub_menu("Callbacks (dangerous! use at your own risk)", "C", AWM_ALL);
1235  awmm->insert_menu_topic("!run_all_cbs_alph", "Call all callbacks (alpha-order)", "a", "", AWM_ALL, makeWindowCallback(callallcallbacks, 0));
1236  awmm->insert_menu_topic("!run_all_cbs_nalph", "Call all callbacks (alpha-reverse)", "l", "", AWM_ALL, makeWindowCallback(callallcallbacks, 1));
1237  awmm->insert_menu_topic("!run_all_cbs_loc", "Call all callbacks (code-order)", "c", "", AWM_ALL, makeWindowCallback(callallcallbacks, 2));
1238  awmm->insert_menu_topic("!run_all_cbs_nloc", "Call all callbacks (code-reverse)", "o", "", AWM_ALL, makeWindowCallback(callallcallbacks, 3));
1239  awmm->insert_menu_topic("!run_all_cbs_rnd", "Call all callbacks (random)", "r", "", AWM_ALL, makeWindowCallback(callallcallbacks, 4));
1240  awmm->sep______________();
1241  awmm->insert_menu_topic("!forget_called_cbs", "Forget called", "F", "", AWM_ALL, makeWindowCallback(callallcallbacks, -1));
1242  awmm->insert_menu_topic("!mark_all_called", "Mark all called", "M", "", AWM_ALL, makeWindowCallback(callallcallbacks, -2));
1243  awmm->sep______________();
1244  {
1245  awmm->insert_sub_menu("Call repeated", "p", AWM_ALL);
1246  awmm->insert_menu_topic("!run_all_cbs_alph_inf", "Call all callbacks (alpha-order repeated)", "a", "", AWM_ALL, makeWindowCallback(callallcallbacks, 8|0));
1247  awmm->insert_menu_topic("!run_all_cbs_nalph_inf", "Call all callbacks (alpha-reverse repeated)", "l", "", AWM_ALL, makeWindowCallback(callallcallbacks, 8|1));
1248  awmm->insert_menu_topic("!run_all_cbs_loc_inf", "Call all callbacks (code-order repeated)", "c", "", AWM_ALL, makeWindowCallback(callallcallbacks, 8|2));
1249  awmm->insert_menu_topic("!run_all_cbs_nloc_inf", "Call all callbacks (code-reverse repeated)", "o", "", AWM_ALL, makeWindowCallback(callallcallbacks, 8|3));
1250  awmm->insert_menu_topic("!run_all_cbs_rnd_inf", "Call all callbacks (random repeated)", "r", "", AWM_ALL, makeWindowCallback(callallcallbacks, 8|4));
1251  awmm->close_sub_menu();
1252  }
1253  awmm->close_sub_menu();
1254  }
1255  awmm->sep______________();
1256 
1257  awmm->insert_menu_topic(awmm->local_id("-client_ntree"), "Start ARB_NTREE as client", "N", "", AWM_ALL, makeWindowCallback(AW_system_async_cb, "arb_ntree :"));
1258 }
1259 
1260 #if 1
1261 void AWT_check_action_ids(AW_root *, const char *) {
1262 }
1263 #else
1264 void AWT_check_action_ids(AW_root *aw_root, const char *suffix) {
1265  // check actions ids (see #428)
1266  // suffix is appended to filenames (needed if one application may have different states)
1267  GB_ERROR error = NULp;
1268  {
1269  SmartPtr<ConstStrArray> action_ids = aw_root->get_action_ids();
1270 
1271  const char *checksdir = GB_path_in_ARBLIB("macros/.checks");
1272  char *save = GBS_global_string_copy("%s/%s%s_action.ids", checksdir, aw_root->program_name, suffix);
1273 
1274  FILE *out = fopen(save, "wt");
1275  if (!out) error = GB_IO_error("writing", save);
1276  else {
1277  for (size_t i = 0; i<action_ids->size(); ++i) {
1278  fprintf(out, "%s\n", (*action_ids)[i]);
1279  }
1280  fclose(out);
1281  }
1282 
1283  if (!error) {
1284  char *expected = GBS_global_string_copy("%s.expected", save);
1285  bool IDs_have_changed = ARB_textfiles_have_difflines(expected, save, 0, TDM_NOT_DIFF_LINECOUNT);
1286  if (IDs_have_changed) error = GBS_global_string("action ids differ from expected (see console)");
1287  free(expected);
1288  }
1289 
1290  if (!error) GB_unlink(save);
1291  free(save);
1292  }
1293  if (error) fprintf(stderr, "AWT_check_action_ids: Error: %s\n", error);
1294 }
1295 #endif
1296 
1297 #endif // DEBUG
1298 
1299 AW_root *AWT_create_root(const char *properties, const char *program, UserActionTracker *user_tracker) {
1300  // Implementation note:
1301  // there probably should be a corresponding cleanup method (e.g. AWT_delete_root)
1302  // which could be used to forget properties database and cleanup advisor.
1303  // see also: destroy_AW_root()
1304 
1305  AW_root *aw_root = new AW_root(properties, program, false, user_tracker);
1306 #if defined(DEBUG)
1307  AWT_announce_properties_to_browser(AW_ROOT_DEFAULT, properties);
1308  // database is never forgotten (i.e. ARB_disconnect_from_db is never called for it)
1309 #endif // DEBUG
1310  init_Advisor(aw_root);
1311  return aw_root;
1312 }
1313 
1314 void AWT_trigger_remote_action(UNFIXED, GBDATA *gb_main, const char *remote_action_spec) {
1320  ConstStrArray appAction;
1321  GBT_split_string(appAction, remote_action_spec, ";", SPLIT_DROPEMPTY);
1322 
1323  GB_ERROR error = NULp;
1324  if (appAction.empty()) {
1325  error = "No action found";
1326  }
1327  else {
1328  for (unsigned a = 0; a<appAction.size() && !error; ++a) {
1329  ConstStrArray cmd;
1330  GBT_split_string(cmd, appAction[a], ":", SPLIT_DROPEMPTY);
1331 
1332  if (cmd.size() != 2) {
1333  error = GBS_global_string("Invalid action '%s'", appAction[a]);
1334  }
1335  else {
1336  const char *app = cmd[0];
1337  const char *action = cmd[1];
1338 
1339  ARB_timeout after(2000, MS);
1340  error = GBT_remote_action_with_timeout(gb_main, app, action, &after, false);
1341  }
1342  }
1343  }
1344 
1345  aw_message_if(error);
1346 }
1347 
1348 // ------------------------
1349 // callback guards
1350 
1351 static void before_callback_guard() {
1352  if (GB_have_error()) {
1353  GB_ERROR error = GB_await_error();
1354  aw_message(GBS_global_string("Error not clear before calling callback\n"
1355  "Unhandled error was:\n"
1356  "%s", error));
1357 #if defined(DEVEL_RALF)
1358  arb_assert(0);
1359 #endif // DEVEL_RALF
1360  }
1361 }
1362 static void after_callback_guard() {
1363  if (GB_have_error()) {
1364  GB_ERROR error = GB_await_error();
1365  aw_message(GBS_global_string("Error not handled by callback!\n"
1366  "Unhandled error was:\n"
1367  "'%s'", error));
1368 #if defined(DEVEL_RALF)
1369  arb_assert(0);
1370 #endif // DEVEL_RALF
1371  }
1372 }
1373 
1377 }
1379  AW_cb::set_AW_postcb_cb(postcb_cb);
1380 }
1381 
void insert_option(AW_label choice_label, const char *mnemonic, const char *var_value, const char *name_of_color=NULp)
#define arb_assert(cond)
Definition: arb_assert.h:245
static void after_callback_guard()
const char * GB_ERROR
Definition: arb_core.h:25
GB_TYPES type
void GBS_restore_global_buffers(GlobalStringBuffers *saved)
Definition: arb_msg.cxx:162
NOT4PERL long GB_calc_structure_size(GBDATA *gbd)
size_t size() const
Definition: arb_strarray.h:85
void init_Advisor(AW_root *awr)
has to be called one time (before calling AW_advice)
Definition: AW_advice.cxx:41
Definition: arbdb.h:65
GBDATA * GB_child(GBDATA *father)
Definition: adquery.cxx:322
return string(buffer, length)
void space()
Definition: test_unit.h:414
bool GB_is_temporary(GBDATA *gbd)
Definition: arbdb.cxx:2304
Definition: arbdb.h:69
void insert_menu_topic(const char *id, const char *name, const char *mnemonic, const char *help_text_, AW_active mask, const WindowCallback &wcb)
Definition: AW_window.cxx:595
void load_xfig(const char *file, bool resize=true)
Definition: AW_window.cxx:720
GB_ERROR GB_add_callback(GBDATA *gbd, GB_CB_TYPE type, const DatabaseCallback &dbcb)
Definition: ad_cb.cxx:356
GBDATA * GB_nextEntry(GBDATA *entry)
Definition: adquery.cxx:339
int ARB_stricmp(const char *s1, const char *s2)
Definition: arb_str.h:28
static list< ArbDisconnectCallback > callbacks
Definition: app.cxx:21
const char * GB_get_type_name(GBDATA *gbd)
Definition: arbdb.cxx:72
GB_ERROR GB_IO_error(const char *action, const char *filename)
Definition: arb_msg.cxx:285
char * ARB_strdup(const char *str)
Definition: arb_string.h:27
char * GB_read_as_string(GBDATA *gbd)
Definition: arbdb.cxx:1060
static void set_AW_cb_guards(AW_cb_struct_guard before, AW_cb_struct_guard after)
Definition: aw_window.hxx:159
long read_int() const
Definition: AW_awar.cxx:184
size_t GBS_shorten_repeated_data(char *data)
Definition: adstring.cxx:357
const char * GBS_global_string(const char *templat,...)
Definition: arb_msg.cxx:203
NOT4PERL GB_ERROR GBT_remote_action_with_timeout(GBDATA *gb_main, const char *application, const char *action_name, const class ARB_timeout *timeout, bool verbose)
Definition: adtools.cxx:792
bool GB_have_error()
Definition: arb_msg.cxx:338
STL namespace.
char * GBS_string_eval(const char *insource, const char *icommand)
Definition: admatch.cxx:699
void AW_POPDOWN(AW_window *window)
Definition: AW_window.cxx:52
bool wrapped() const
Definition: hexdump.hxx:101
char * ARB_strpartdup(const char *start, const char *end)
Definition: arb_string.h:51
int GB_unlink(const char *path)
Definition: arb_file.cxx:188
char * program_name
Definition: aw_root.hxx:113
bool is_mapped() const
Definition: aw_awar.hxx:153
GBDATA * GB_get_father(GBDATA *gbd)
Definition: arbdb.cxx:1722
void update_option_menu()
bool empty() const
Definition: arb_strarray.h:86
AW_awar * add_callback(const RootCallback &cb)
Definition: AW_awar.cxx:231
struct Unfixed_cb_parameter * UNFIXED
Definition: cb_base.h:15
void insert(const char *displayed, const AW_scalar &value)
Definition: AW_select.cxx:380
static HelixNrInfo * start
POS_TREE1 * father
Definition: probe_tree.h:39
const char * read_char_pntr() const
Definition: AW_awar.cxx:168
GB_ERROR GB_await_error()
Definition: arb_msg.cxx:342
static AW_root * SINGLETON
Definition: aw_root.hxx:102
AliDataPtr after(AliDataPtr data, size_t pos)
Definition: insdel.cxx:593
Definition: arbdb.h:67
long GB_read_count(GBDATA *gbd)
Definition: arbdb.cxx:758
Definition: arbdb.h:78
GB_TYPES GB_read_type(GBDATA *gbd)
Definition: arbdb.cxx:1643
GB_ERROR rewrite_int(long aw_int)
GB_CSTR GB_read_key_pntr(GBDATA *gbd)
Definition: arbdb.cxx:1656
char * GB_get_callback_info(GBDATA *gbd)
Definition: ad_cb.cxx:226
GB_CSTR GB_read_pntr(GBDATA *gbd)
Definition: arbdb.cxx:877
long GB_number_of_subentries(GBDATA *gbd)
Definition: arbdb.cxx:2892
Generic smart pointer.
Definition: smartptr.h:149
int GB_read_security_write(GBDATA *gbd)
Definition: arbdb.cxx:1572
static void set_AW_postcb_cb(AW_postcb_cb postcb_cb)
Definition: aw_window.hxx:163
size_t mem_needed_for_dump(size_t bytes) const
Definition: hexdump.hxx:103
void GB_clear_error()
Definition: arb_msg.cxx:354
Definition: arbdb.h:64
#define aw_assert(bed)
Definition: aw_position.hxx:29
void create_menu(const char *name, const char *mnemonic, AW_active mask=AWM_ALL)
Definition: AW_window.cxx:472
const char * GBS_readable_size(unsigned long long size, const char *unit_suffix)
Definition: arb_misc.cxx:23
void touch()
Definition: AW_awar.cxx:207
static void error(const char *msg)
Definition: mkptypes.cxx:96
GBDATA * GB_get_root(GBDATA *gbd)
Definition: arbdb.cxx:1740
char * str
Definition: defines.h:20
void dump_to(GBS_strstruct &buf, const char *mem, size_t size) const
Definition: hexdump.hxx:115
void AWT_trigger_remote_action(UNFIXED, GBDATA *gb_main, const char *remote_action_spec)
#define cmp(h1, h2)
Definition: admap.cxx:50
GBQUARK GB_get_quark(GBDATA *gbd)
Definition: arbdb.cxx:1703
char * read_string() const
Definition: AW_awar.cxx:198
GB_CSTR GB_path_in_ARBLIB(const char *relative_path)
Definition: adsocket.cxx:1147
AW_awar * awar_no_error(const char *awar)
end timer stuff
Definition: AW_root.cxx:549
AW_awar * awar(const char *awar)
Definition: AW_root.cxx:554
void insert_sub_menu(const char *name, const char *mnemonic, AW_active mask=AWM_ALL)
Definition: AW_window.cxx:645
Definition: arbdb.h:86
void AWT_install_cb_guards()
bool ARB_textfiles_have_difflines(const char *file1, const char *file2, int expected_difflines, TextDiffMode tdmode)
Definition: arb_diff.cxx:250
char * history
Definition: AW_help.cxx:76
static void before_callback_guard()
void clear_option_menu(AW_option_menu_struct *oms)
long int flag
Definition: f2c.h:39
int GB_read_security_read(GBDATA *gbd)
Definition: arbdb.cxx:1576
int GB_read_security_delete(GBDATA *gbd)
Definition: arbdb.cxx:1580
Definition: arbdb.h:72
AW_awar * awar_int(const char *var_name, long default_value=0, AW_default default_file=AW_ROOT_DEFAULT)
Definition: AW_root.cxx:580
Definition: arb_sleep.h:30
GBDATA * gb_var
Definition: aw_awar.hxx:97
void nprintf(size_t maxlen, const char *templat,...) __ATTR__FORMAT_MEMBER(2)
Definition: arb_strbuf.cxx:29
void GB_remove_callback(GBDATA *gbd, GB_CB_TYPE type, const DatabaseCallback &dbcb)
Definition: ad_cb.cxx:360
void close_sub_menu()
Definition: AW_window.cxx:486
const char * GB_get_db_path(GBDATA *gbd)
Definition: adTest.cxx:14
void aw_message(const char *msg)
Definition: AW_status.cxx:1142
AW_option_menu_struct * create_option_menu(const char *awar_name)
AW_root * get_root()
Definition: aw_window.hxx:359
GlobalStringBuffers * GBS_store_global_buffers()
Definition: arb_msg.cxx:156
#define NULp
Definition: cxxforward.h:116
GB_ERROR write_string(const char *aw_string)
static char * command
Definition: arb_a2ps.c:319
void GBT_split_string(ConstStrArray &dest, const char *namelist, const char *separator, SplitMode mode)
Definition: arb_strarray.h:223
#define offset(field)
Definition: GLwDrawA.c:73
void AWT_install_postcb_cb(AW_postcb_cb postcb_cb)
bool ARB_strBeginsWith(const char *str, const char *with)
Definition: arb_str.h:42
void sep______________()
Definition: AW_window.cxx:753
const char * local_id(const char *id) const
Definition: AW_window.cxx:747
void ARB_atdisconnect_callback(const ArbDisconnectCallback &cb)
Definition: app.cxx:23
GB_TYPES
Definition: arbdb.h:62
GBDATA * GB_nextChild(GBDATA *child)
Definition: adquery.cxx:326
GB_transaction ta(gb_var)
GBDATA * gb_main
Definition: adname.cxx:32
Definition: arbdb.h:71
AW_awar * awar_string(const char *var_name, const char *default_value="", AW_default default_file=AW_ROOT_DEFAULT)
Definition: AW_root.cxx:570
GBDATA * GB_search(GBDATA *gbd, const char *fieldpath, GB_TYPES create)
Definition: adquery.cxx:531
void(* AW_postcb_cb)(AW_window *)
Definition: aw_base.hxx:24
GB_CB_TYPE
Definition: arbdb_base.h:46
static int info[maxsites+1]
AW_root * AWT_create_root(const char *properties, const char *program, UserActionTracker *user_tracker)
void GBT_splitNdestroy_string(ConstStrArray &names, char *&namelist, const char *separator, SplitMode mode)
#define AW_ROOT_DEFAULT
Definition: aw_base.hxx:106
GB_ERROR write_int(long aw_int)
GBDATA * GB_entry(GBDATA *father, const char *key)
Definition: adquery.cxx:334
void AW_system_async_cb(AW_window *, const char *command)
Definition: aw_system.hxx:24
void aw_message_if(GB_ERROR error)
Definition: aw_msg.hxx:21
char * GBS_global_string_copy(const char *templat,...)
Definition: arb_msg.cxx:194
Definition: arbdb.h:66
GB_ERROR rewrite_string(const char *aw_string)
bool operator<(const GEN_positionPtr &A, const GEN_positionPtr &B)
Definition: DBwriter.cxx:250
bool GB_in_temporary_branch(GBDATA *gbd)
Definition: arbdb.cxx:2310