| 1 | // =============================================================== // |
|---|
| 2 | // // |
|---|
| 3 | // File : ad_config.cxx // |
|---|
| 4 | // Purpose : editor configurations // |
|---|
| 5 | // // |
|---|
| 6 | // Coded by Ralf Westram (coder@reallysoft.de) in May 2005 // |
|---|
| 7 | // Institute of Microbiology (Technical University Munich) // |
|---|
| 8 | // http://www.arb-home.de/ // |
|---|
| 9 | // // |
|---|
| 10 | // =============================================================== // |
|---|
| 11 | |
|---|
| 12 | #include "gb_local.h" |
|---|
| 13 | |
|---|
| 14 | #include <ad_config.h> |
|---|
| 15 | #include <arbdbt.h> |
|---|
| 16 | |
|---|
| 17 | #include <arb_strbuf.h> |
|---|
| 18 | #include <arb_defs.h> |
|---|
| 19 | #include <arb_strarray.h> |
|---|
| 20 | |
|---|
| 21 | void GBT_get_configuration_names(ConstStrArray& configNames, GBDATA *gb_main) { |
|---|
| 22 | /* returns existing configurations in 'configNames' |
|---|
| 23 | * Note: automatically generates names for configs w/o legal name. |
|---|
| 24 | */ |
|---|
| 25 | |
|---|
| 26 | GB_transaction ta(gb_main); |
|---|
| 27 | GBDATA *gb_config_data = GB_search(gb_main, CONFIG_DATA_PATH, GB_CREATE_CONTAINER); |
|---|
| 28 | |
|---|
| 29 | if (gb_config_data) { |
|---|
| 30 | int unnamed_count = 0; |
|---|
| 31 | |
|---|
| 32 | configNames.reserve(GB_number_of_subentries(gb_config_data)); |
|---|
| 33 | |
|---|
| 34 | for (GBDATA *gb_config = GB_entry(gb_config_data, CONFIG_ITEM); |
|---|
| 35 | gb_config; |
|---|
| 36 | gb_config = GB_nextEntry(gb_config)) |
|---|
| 37 | { |
|---|
| 38 | const char *name = GBT_read_char_pntr(gb_config, "name"); |
|---|
| 39 | |
|---|
| 40 | if (!name || name[0] == 0) { // no name or empty name |
|---|
| 41 | char *new_name = GBS_global_string_copy("<unnamed%i>", ++unnamed_count); |
|---|
| 42 | GB_ERROR error = GBT_write_string(gb_config, "name", new_name); |
|---|
| 43 | |
|---|
| 44 | if (error) { |
|---|
| 45 | GB_warningf("Failed to rename unnamed configuration to '%s'", new_name); |
|---|
| 46 | freenull(new_name); |
|---|
| 47 | name = NULL; |
|---|
| 48 | } |
|---|
| 49 | else { |
|---|
| 50 | name = GBT_read_char_pntr(gb_config, "name"); |
|---|
| 51 | } |
|---|
| 52 | } |
|---|
| 53 | |
|---|
| 54 | if (name) configNames.put(name); |
|---|
| 55 | } |
|---|
| 56 | } |
|---|
| 57 | } |
|---|
| 58 | |
|---|
| 59 | GBDATA *GBT_find_configuration(GBDATA *gb_main, const char *name) { |
|---|
| 60 | GBDATA *gb_config_data = GB_search(gb_main, CONFIG_DATA_PATH, GB_DB); |
|---|
| 61 | GBDATA *gb_config_name = GB_find_string(gb_config_data, "name", name, GB_IGNORE_CASE, SEARCH_GRANDCHILD); |
|---|
| 62 | return gb_config_name ? GB_get_father(gb_config_name) : 0; |
|---|
| 63 | } |
|---|
| 64 | |
|---|
| 65 | GBDATA *GBT_create_configuration(GBDATA *gb_main, const char *name) { |
|---|
| 66 | GBDATA *gb_config = GBT_find_configuration(gb_main, name); |
|---|
| 67 | if (!gb_config) { |
|---|
| 68 | GBDATA *gb_config_data = GB_search(gb_main, CONFIG_DATA_PATH, GB_DB); |
|---|
| 69 | |
|---|
| 70 | gb_config = GB_create_container(gb_config_data, CONFIG_ITEM); // create new container |
|---|
| 71 | if (gb_config) { |
|---|
| 72 | GB_ERROR error = GBT_write_string(gb_config, "name", name); |
|---|
| 73 | if (error) GB_export_error(error); |
|---|
| 74 | } |
|---|
| 75 | } |
|---|
| 76 | return gb_config; |
|---|
| 77 | } |
|---|
| 78 | |
|---|
| 79 | void GBT_free_configuration_data(GBT_config *data) { |
|---|
| 80 | free(data->top_area); |
|---|
| 81 | free(data->middle_area); |
|---|
| 82 | free(data); |
|---|
| 83 | } |
|---|
| 84 | |
|---|
| 85 | GBT_config *GBT_load_configuration_data(GBDATA *gb_main, const char *name, GB_ERROR *error) { |
|---|
| 86 | GBT_config *config = 0; |
|---|
| 87 | |
|---|
| 88 | *error = GB_push_transaction(gb_main); |
|---|
| 89 | GBDATA *gb_config = GBT_find_configuration(gb_main, name); |
|---|
| 90 | |
|---|
| 91 | if (!gb_config) { |
|---|
| 92 | *error = GBS_global_string("No such configuration '%s'", name); |
|---|
| 93 | } |
|---|
| 94 | else { |
|---|
| 95 | config = (GBT_config*)GB_calloc(1, sizeof(*config)); |
|---|
| 96 | config->top_area = GBT_read_string(gb_config, "top_area"); |
|---|
| 97 | config->middle_area = GBT_read_string(gb_config, "middle_area"); |
|---|
| 98 | |
|---|
| 99 | if (!config->top_area || !config->middle_area) { |
|---|
| 100 | GBT_free_configuration_data(config); |
|---|
| 101 | config = 0; |
|---|
| 102 | *error = GBS_global_string("Configuration '%s' is corrupted (Reason: %s)", |
|---|
| 103 | name, GB_await_error()); |
|---|
| 104 | } |
|---|
| 105 | } |
|---|
| 106 | |
|---|
| 107 | *error = GB_end_transaction(gb_main, *error); |
|---|
| 108 | return config; |
|---|
| 109 | } |
|---|
| 110 | |
|---|
| 111 | GB_ERROR GBT_save_configuration_data(GBT_config *config, GBDATA *gb_main, const char *name) { |
|---|
| 112 | GB_ERROR error = 0; |
|---|
| 113 | GBDATA *gb_config; |
|---|
| 114 | |
|---|
| 115 | GB_push_transaction(gb_main); |
|---|
| 116 | |
|---|
| 117 | gb_config = GBT_create_configuration(gb_main, name); |
|---|
| 118 | if (!gb_config) { |
|---|
| 119 | error = GBS_global_string("Can't create configuration '%s' (Reason: %s)", name, GB_await_error()); |
|---|
| 120 | } |
|---|
| 121 | else { |
|---|
| 122 | error = GBT_write_string(gb_config, "top_area", config->top_area); |
|---|
| 123 | if (!error) error = GBT_write_string(gb_config, "middle_area", config->middle_area); |
|---|
| 124 | |
|---|
| 125 | if (error) error = GBS_global_string("%s (in configuration '%s')", error, name); |
|---|
| 126 | } |
|---|
| 127 | |
|---|
| 128 | return GB_end_transaction(gb_main, error); |
|---|
| 129 | } |
|---|
| 130 | |
|---|
| 131 | GBT_config_parser *GBT_start_config_parser(const char *config_string) { |
|---|
| 132 | GBT_config_parser *parser = (GBT_config_parser*)GB_calloc(1, sizeof(*parser)); |
|---|
| 133 | |
|---|
| 134 | parser->config_string = nulldup(config_string); |
|---|
| 135 | parser->parse_pos = 0; |
|---|
| 136 | |
|---|
| 137 | return parser; |
|---|
| 138 | } |
|---|
| 139 | |
|---|
| 140 | GBT_config_item *GBT_create_config_item() { |
|---|
| 141 | GBT_config_item *item = (GBT_config_item*)GB_calloc(1, sizeof(*item)); |
|---|
| 142 | item->type = CI_UNKNOWN; |
|---|
| 143 | item->name = 0; |
|---|
| 144 | return item; |
|---|
| 145 | } |
|---|
| 146 | |
|---|
| 147 | void GBT_free_config_item(GBT_config_item *item) { |
|---|
| 148 | free(item->name); |
|---|
| 149 | free(item); |
|---|
| 150 | } |
|---|
| 151 | |
|---|
| 152 | GB_ERROR GBT_parse_next_config_item(GBT_config_parser *parser, GBT_config_item *item) { |
|---|
| 153 | // the passed 'item' gets filled with parsed data from the config string |
|---|
| 154 | GB_ERROR error = 0; |
|---|
| 155 | |
|---|
| 156 | const char *str = parser->config_string; |
|---|
| 157 | int pos = parser->parse_pos; |
|---|
| 158 | |
|---|
| 159 | freenull(item->name); |
|---|
| 160 | item->type = CI_END_OF_CONFIG; |
|---|
| 161 | |
|---|
| 162 | if (str[pos]) { // if not at 0-byte |
|---|
| 163 | char label = str[pos+1]; |
|---|
| 164 | item->type = CI_UNKNOWN; |
|---|
| 165 | |
|---|
| 166 | switch (label) { |
|---|
| 167 | case 'L': item->type = CI_SPECIES; break; |
|---|
| 168 | case 'S': item->type = CI_SAI; break; |
|---|
| 169 | case 'F': item->type = CI_FOLDED_GROUP; break; |
|---|
| 170 | case 'G': item->type = CI_GROUP; break; |
|---|
| 171 | case 'E': item->type = CI_CLOSE_GROUP; break; |
|---|
| 172 | default: item->type = CI_UNKNOWN; break; |
|---|
| 173 | } |
|---|
| 174 | |
|---|
| 175 | if (item->type == CI_CLOSE_GROUP) { |
|---|
| 176 | pos += 2; |
|---|
| 177 | } |
|---|
| 178 | else { |
|---|
| 179 | const char *start_of_name = str+pos+2; |
|---|
| 180 | const char *behind_name = strchr(start_of_name, '\1'); |
|---|
| 181 | |
|---|
| 182 | if (!behind_name) behind_name = strchr(start_of_name, '\0'); // eos |
|---|
| 183 | gb_assert(behind_name); |
|---|
| 184 | |
|---|
| 185 | char *data = GB_strpartdup(start_of_name, behind_name-1); |
|---|
| 186 | if (item->type == CI_UNKNOWN) { |
|---|
| 187 | error = GBS_global_string_copy("Unknown flag '%c' (followed by '%s')", label, data); |
|---|
| 188 | free(data); |
|---|
| 189 | } |
|---|
| 190 | else { |
|---|
| 191 | item->name = data; |
|---|
| 192 | pos = behind_name-str; |
|---|
| 193 | } |
|---|
| 194 | } |
|---|
| 195 | |
|---|
| 196 | if (error) { // stop parser |
|---|
| 197 | const char *end_of_config = strchr(str+pos, '\0'); |
|---|
| 198 | pos = end_of_config-str; |
|---|
| 199 | gb_assert(str[pos] == 0); |
|---|
| 200 | } |
|---|
| 201 | |
|---|
| 202 | parser->parse_pos = pos; |
|---|
| 203 | } |
|---|
| 204 | return error; |
|---|
| 205 | } |
|---|
| 206 | |
|---|
| 207 | void GBT_append_to_config_string(const GBT_config_item *item, GBS_strstruct *strstruct) { |
|---|
| 208 | // strstruct has to be created by GBS_stropen() |
|---|
| 209 | |
|---|
| 210 | gb_assert((item->type & (CI_UNKNOWN|CI_END_OF_CONFIG)) == 0); |
|---|
| 211 | |
|---|
| 212 | char prefix[] = "\1?"; |
|---|
| 213 | if (item->type == CI_CLOSE_GROUP) { |
|---|
| 214 | prefix[1] = 'E'; |
|---|
| 215 | GBS_strcat(strstruct, prefix); |
|---|
| 216 | } |
|---|
| 217 | else { |
|---|
| 218 | char label = 0; |
|---|
| 219 | switch (item->type) { |
|---|
| 220 | case CI_SPECIES: label = 'L'; break; |
|---|
| 221 | case CI_SAI: label = 'S'; break; |
|---|
| 222 | case CI_GROUP: label = 'G'; break; |
|---|
| 223 | case CI_FOLDED_GROUP: label = 'F'; break; |
|---|
| 224 | |
|---|
| 225 | default: gb_assert(0); break; |
|---|
| 226 | } |
|---|
| 227 | prefix[1] = label; |
|---|
| 228 | GBS_strcat(strstruct, prefix); |
|---|
| 229 | GBS_strcat(strstruct, item->name); |
|---|
| 230 | } |
|---|
| 231 | } |
|---|
| 232 | |
|---|
| 233 | |
|---|
| 234 | void GBT_free_config_parser(GBT_config_parser *parser) { |
|---|
| 235 | free(parser->config_string); |
|---|
| 236 | free(parser); |
|---|
| 237 | } |
|---|
| 238 | |
|---|
| 239 | #if defined(DEBUG) |
|---|
| 240 | void GBT_test_config_parser(GBDATA *gb_main) { |
|---|
| 241 | ConstStrArray config_names; |
|---|
| 242 | GBT_get_configuration_names(config_names, gb_main); |
|---|
| 243 | if (!config_names.empty()) { |
|---|
| 244 | int count; |
|---|
| 245 | for (count = 0; config_names[count]; ++count) { |
|---|
| 246 | const char *config_name = config_names[count]; |
|---|
| 247 | GBT_config *config; |
|---|
| 248 | GB_ERROR error = 0; |
|---|
| 249 | |
|---|
| 250 | printf("Testing configuration '%s':\n", config_name); |
|---|
| 251 | config = GBT_load_configuration_data(gb_main, config_name, &error); |
|---|
| 252 | if (!config) { |
|---|
| 253 | gb_assert(error); |
|---|
| 254 | printf("* Error loading config: %s\n", error); |
|---|
| 255 | } |
|---|
| 256 | else { |
|---|
| 257 | int area; |
|---|
| 258 | |
|---|
| 259 | gb_assert(!error); |
|---|
| 260 | printf("* Successfully loaded\n"); |
|---|
| 261 | |
|---|
| 262 | for (area = 0; area<2 && !error; ++area) { |
|---|
| 263 | const char *area_name = area ? "middle_area" : "top_area"; |
|---|
| 264 | const char *area_config_def = area ? config->middle_area : config->top_area; |
|---|
| 265 | GBT_config_parser *parser = GBT_start_config_parser(area_config_def); |
|---|
| 266 | GBT_config_item *item = GBT_create_config_item(); |
|---|
| 267 | GBS_strstruct *new_config = GBS_stropen(1000); |
|---|
| 268 | char *new_config_str; |
|---|
| 269 | |
|---|
| 270 | gb_assert(parser); |
|---|
| 271 | printf("* Created parser for '%s'\n", area_name); |
|---|
| 272 | |
|---|
| 273 | while (1) { |
|---|
| 274 | error = GBT_parse_next_config_item(parser, item); |
|---|
| 275 | if (error || item->type == CI_END_OF_CONFIG) break; |
|---|
| 276 | |
|---|
| 277 | printf(" - %i %s\n", item->type, item->name ? item->name : "[close group]"); |
|---|
| 278 | GBT_append_to_config_string(item, new_config); |
|---|
| 279 | } |
|---|
| 280 | |
|---|
| 281 | GBT_free_config_item(item); |
|---|
| 282 | new_config_str = GBS_strclose(new_config); |
|---|
| 283 | |
|---|
| 284 | if (error) printf("* Parser error: %s\n", error); |
|---|
| 285 | else { |
|---|
| 286 | if (strcmp(area_config_def, new_config_str) == 0) { |
|---|
| 287 | printf("* Re-Created config is identical to original\n"); |
|---|
| 288 | } |
|---|
| 289 | else { |
|---|
| 290 | printf("* Re-Created config is NOT identical to original:\n" |
|---|
| 291 | " - original : '%s'\n" |
|---|
| 292 | " - recreated: '%s'\n", |
|---|
| 293 | area_config_def, |
|---|
| 294 | new_config_str); |
|---|
| 295 | } |
|---|
| 296 | } |
|---|
| 297 | |
|---|
| 298 | GBT_free_config_parser(parser); |
|---|
| 299 | free(new_config_str); |
|---|
| 300 | } |
|---|
| 301 | } |
|---|
| 302 | GBT_free_configuration_data(config); |
|---|
| 303 | } |
|---|
| 304 | } |
|---|
| 305 | } |
|---|
| 306 | #endif // DEBUG |
|---|
| 307 | |
|---|
| 308 | // -------------------------------------------------------------------------------- |
|---|
| 309 | |
|---|
| 310 | #ifdef UNIT_TESTS |
|---|
| 311 | #include <test_unit.h> |
|---|
| 312 | |
|---|
| 313 | void TEST_GBT_get_configuration_names() { |
|---|
| 314 | GB_shell shell; |
|---|
| 315 | GBDATA *gb_main = GB_open("nosuch.arb", "c"); |
|---|
| 316 | |
|---|
| 317 | { |
|---|
| 318 | GB_transaction ta(gb_main); |
|---|
| 319 | |
|---|
| 320 | const char *configs[] = { "arb", "BASIC", "Check it", "dummy" }; |
|---|
| 321 | for (size_t i = 0; i<ARRAY_ELEMS(configs); ++i) { |
|---|
| 322 | TEST_EXPECT_RESULT__NOERROREXPORTED(GBT_create_configuration(gb_main, configs[i])); |
|---|
| 323 | } |
|---|
| 324 | |
|---|
| 325 | ConstStrArray cnames; |
|---|
| 326 | GBT_get_configuration_names(cnames, gb_main); |
|---|
| 327 | |
|---|
| 328 | TEST_EXPECT_EQUAL(cnames.size(), 4U); |
|---|
| 329 | |
|---|
| 330 | char *joined = GBT_join_names(cnames, '*'); |
|---|
| 331 | TEST_EXPECT_EQUAL(joined, "arb*BASIC*Check it*dummy"); |
|---|
| 332 | free(joined); |
|---|
| 333 | } |
|---|
| 334 | |
|---|
| 335 | GB_close(gb_main); |
|---|
| 336 | } |
|---|
| 337 | |
|---|
| 338 | #endif // UNIT_TESTS |
|---|