+/* Editing state flags set / checked in tokens_into_entries(). */
+enum flag
+{
+ NAME_SET = 0x02,
+ WIDTH_SET = 0x04,
+ HEIGHT_SET = 0x08,
+ BREAK_SET = 0x10,
+ FOCUS_SET = 0x02,
+ READY_WIN = NAME_SET | WIDTH_SET | HEIGHT_SET | BREAK_SET,
+ READY_ORD = 0x00,
+ READY_KBD = 0x00
+};
+
+
+
+/* Used in load_interface_conf() to temporarily store designated values for
+ * world.winDB.order and world.winDB.active. If those were set right away
+ * without all windows having already been initialized, orderly exits on error
+ * would be impossible, for windows are cleaned out by working toggling them off
+ * piece by peice following these values in unload_interfac_conf().
+ */
+static char * tmp_order;
+static char tmp_active;
+
+
+
+/* Write into "file" keybindings in "kbdb" in config file format. */
+static void write_keybindings(FILE * file, struct KeyBindingDB * kbdb);
+
+/* Write into file "val" interpreted as pointing either to string (type = 's'),
+ * int16 ('i') or char ('c'), prefixed with "prefix" and put into single quotes
+ * if 0 != "quotes".
+ */
+static void write_def(FILE * file, char * prefix, uint8_t quotes, char * val,
+ char type);
+
+/* Read in "token0" and "token1" as interface config definition lines, apply
+ * definitions to WinDB, KeyBindingsDBs and tmp_order/tmp_active. If argument is
+ * "KEY", get third token via token_from_line() for complete keybinding.
+ */
+static void tokens_into_entries(char * token0, char * token1);
+
+/* If "win" points to non-NULL Win struct, append to it to WinDB.wins array and
+ * add its ID to world.winDB.ids.
+ */
+static void write_if_win(struct Win ** win);
+
+/* Start block setting data for window "token1" id if "token0" == "str_win".*/
+static uint8_t start_win(char * token0, char * token1, char * str_win,
+ uint8_t * win_flags, struct Win ** win);
+
+/* Start block setting window order and focus if "token0" = "str_ord".
+ * "tmp_order" stores the designated world.winDB.order string (only later to
+ * be realized in load_interface_conf() when all windows have been initialized),
+ * as does "tmp_active" for the designated world.winDB.active char, defaulting
+ * to the first char in the window order string if not otherwise specified.
+ */
+static uint8_t start_ord(char * token0, char * token1, char * str_ord,
+ uint8_t * ord_flags);
+
+/* Start block setting non-window-specific keybindings if "token0" == "str_kbd".
+ * "token1" must be "global", "wingeom" or "winkeys" to set KeyBindingDB to edit
+ * ("kbdb") to world.kb_global, world.kb_wingeom or world.kb_winkeys.
+ */
+static uint8_t start_kbd(char * token0, char * token1, char * str_kbd,
+ uint8_t * kbd_flags, struct KeyBindingDB ** kbdb);
+
+/* Helper to tokens_into_entries(), sets specific entry member data. "token0"
+ * and the state of flags determine what entry member to edit; "win" and "kbdb"
+ * are entries to write "token1" data into (as is the global "tmp_active"). If
+ * "token0" is "KEY", a keybinding is defined and "token2" is read / must not be
+ * NULL. In that case, the line read is checked against having a 4th token.
+ */
+static uint8_t set_members(char * token0, char * token1, char * token2,
+ uint8_t * win_flags, uint8_t * ord_flags,
+ uint8_t kbd_flags,
+ struct Win * win, struct KeyBindingDB * kbdb);
+
+/* Add keybinding defined in "token1" as keycode and "token2" as command to
+ * "kbdb" if "flags" are set to EDIT_STARTED.
+ */
+static void set_keybindings(char * token1, char * token2, uint8_t flags,
+ struct KeyBindingDB * kbdb);
+
+
+
+static void write_keybindings(FILE * file, struct KeyBindingDB * kbdb)
+{
+ char * f_name = "write_keybindings()";
+ char * sep = " ";
+ char * tok0 = "KEY";
+ uint8_t i_kb;
+ for (i_kb = 0; i_kb < kbdb->n_of_kbs; i_kb++)
+ {
+ size_t size = strlen(tok0) + strlen(sep) + 3 + strlen(sep)
+ + strlen(kbdb->kbs[i_kb].command->dsc_short) + 1 + 1;
+ char * line = try_malloc(size, f_name);
+ int test = snprintf(line, size, "%s%s%d%s%s\n",
+ tok0, sep, kbdb->kbs[i_kb].keycode, sep,
+ kbdb->kbs[i_kb].command->dsc_short);
+ exit_trouble(test < 0, f_name, "snprintf()");
+ try_fwrite(line, sizeof(char), strlen(line), file, f_name);
+ free(line);
+ }
+}
+
+
+
+static void write_def(FILE * file, char * prefix, uint8_t quotes, char * val,
+ char type)
+{
+ char * f_name = "write_def()";
+ char * val_str;
+ int test_val_str = 1;
+ if ('s' == type)
+ {
+ val_str = strdup(val);
+ }
+ if ('i' == type)
+ {
+ size_t size_val_str = 6 + 1;
+ val_str = try_malloc(size_val_str, f_name);
+ test_val_str = snprintf(val_str, size_val_str, "%d", (int16_t) *val);
+ }
+ else if ('c' == type)
+ {
+ size_t size_val_str = 1 + 1;
+ val_str = try_malloc(size_val_str, f_name);
+ test_val_str = snprintf(val_str, size_val_str, "%c", * val);
+ }
+ exit_trouble(test_val_str < 0, f_name, "snprintf()");
+ char * quote = quotes ? "'": "";
+ char * affix = "\n";
+ size_t size = strlen(prefix) + strlen(val_str) + (2 * strlen(quote))
+ + strlen(affix) + 1;
+ char * line = try_malloc(size, f_name);
+ int test = snprintf(line, size, "%s%s%s%s%s",
+ prefix, quote, val_str, quote, affix);
+ exit_trouble(test < 0, f_name, "snprintf()");
+ free(val_str);
+ try_fwrite(line, sizeof(char), strlen(line), file, f_name);
+ free(line);
+}
+
+
+
+static void tokens_into_entries(char * token0, char * token1)
+{
+ char * str_win = "WINDOW";
+ char * str_ord = "WIN_ORDER";
+ char * str_kbd = "KEYBINDINGS";
+ static uint8_t win_flags = READY_WIN;
+ static uint8_t ord_flags = READY_ORD;
+ static uint8_t kbd_flags = READY_KBD;
+ static struct Win * win = NULL;
+ static struct KeyBindingDB * kbdb = NULL;
+ if (!token0 || !strcmp(token0, str_win) || !strcmp(token0, str_ord)
+ || !strcmp(token0, str_kbd))
+ {
+ err_line( ((win_flags & READY_WIN) ^ READY_WIN)
+ || ((ord_flags & READY_ORD) ^ READY_ORD)
+ || ((kbd_flags & READY_KBD) ^ READY_KBD),
+ "Last definition block not yet finished yet.");
+ write_if_win(&win);
+ ord_flags = READY_ORD;
+ win_flags = READY_WIN;
+ kbd_flags = READY_KBD;
+ }
+ if (!token0)
+ {
+ return;
+ }
+ char * token2 = token_from_line(NULL);
+ err_line(strcmp(token0, "KEY") && NULL != token2, "Too many values.");
+ if ( start_win(token0, token1, str_win, &win_flags, &win)
+ || start_ord(token0, token1, str_ord, &ord_flags)
+ || start_kbd(token0, token1, str_kbd, &kbd_flags, &kbdb)
+ || set_members(token0, token1, token2, &win_flags, &ord_flags,
+ kbd_flags, win, kbdb));
+ else
+ {
+ err_line(1, "Unknown argument.");
+ }
+}
+
+
+
+static void write_if_win(struct Win ** win)
+{
+ char * f_name = "write_if_win()";
+ if (*win)
+ {
+ (*win)->target_height_type = (0 >= (*win)->target_height);
+ (*win)->target_width_type = (0 >= (*win)->target_width);;
+ size_t old_ids_size = strlen(world.winDB.ids);
+ size_t new_size = old_ids_size + 1 + 1;
+ char * new_ids = try_malloc(new_size, f_name);
+ int test = snprintf(new_ids,new_size,"%s%c",world.winDB.ids,(*win)->id);
+ exit_trouble(test < 0, f_name, "snprintf()");
+ free(world.winDB.ids);
+ world.winDB.ids = new_ids;
+ array_append(old_ids_size, sizeof(struct Win), *win,
+ (void **) &world.winDB.wins);
+ free(*win);
+ *win = NULL;
+ }
+}
+
+
+
+static uint8_t start_win(char * token0, char * token1, char * str_win,
+ uint8_t * win_flags, struct Win ** win)
+{
+ if (strcmp(token0, str_win))
+ {
+ return 0;
+ }
+ char * f_name = "start_win()";
+ char * err_uniq = "Declaration of ID already used.";
+ *win_flags = EDIT_STARTED;
+ *win = try_malloc(sizeof(struct Win), f_name);
+ memset(*win, 0, sizeof(struct Win));
+ err_line(1 != strlen(token1), "Value must be single ASCII character.");
+ err_line( world.winDB.ids
+ && (NULL != strchr(world.winDB.ids, token1[0])), err_uniq);
+ (*win)->id = token1[0];
+ return 1;
+}
+
+
+
+static uint8_t start_ord(char * token0, char * token1, char * str_ord,
+ uint8_t * ord_flags)
+{
+ if (strcmp(token0, str_ord))
+ {
+ return 0;
+ }
+ *ord_flags = EDIT_STARTED;
+ uint32_t i = 0;
+ for (; i < strlen(token1); i++)
+ {
+ err_line(!strchr(world.winDB.legal_ids, token1[i]), "Illegal ID(s).");
+ }
+ free(tmp_order);
+ tmp_order = strdup(token1);
+ if (0 < strlen(tmp_order))
+ {
+ tmp_active = tmp_order[0];
+ }
+ return 1;
+}
+
+
+
+static uint8_t start_kbd(char * token0, char * token1, char * str_kbd,
+ uint8_t * kbd_flags, struct KeyBindingDB ** kbdb)
+{
+ if (strcmp(token0, str_kbd))
+ {
+ return 0;
+ }
+ *kbd_flags = EDIT_STARTED;
+ if (!strcmp(token1, "global"))
+ {
+ *kbdb = &world.kb_global;
+ }
+ else if (!strcmp(token1, "wingeom"))
+ {
+ *kbdb = &world.kb_wingeom;
+ }
+ else if (!strcmp(token1, "winkeys"))
+ {
+ *kbdb = &world.kb_winkeys;
+ }
+ else
+ {
+ err_line(1, "Value must be 'global', 'wingeom' or 'winkeys'.");
+ }
+ return 1;
+}
+
+
+
+static uint8_t set_members(char * token0, char * token1, char * token2,
+ uint8_t * win_flags, uint8_t * ord_flags,
+ uint8_t kbd_flags,
+ struct Win * win, struct KeyBindingDB * kbdb)
+{
+ if ( set_val(token0, token1, "NAME", win_flags,
+ NAME_SET, 's', (char *) &win->title)
+ || set_val(token0, token1, "WIDTH", win_flags,
+ WIDTH_SET, 'i', (char *) &win->target_width)
+ || set_val(token0, token1, "HEIGHT", win_flags,
+ HEIGHT_SET, 'i', (char *) &win->target_height));
+ else if (set_val(token0, token1, "BREAK", win_flags,
+ BREAK_SET, '8', (char *) &win->linebreak))
+ {
+ err_line(2 < win->linebreak, "Value must be 0, 1 or 2.");
+ }
+ else if (set_val(token0, token1, "WIN_FOCUS", ord_flags,
+ FOCUS_SET, 'c', &tmp_active))
+ {
+ char * err_null = "Value not empty as it should be.";
+ char * err_outside = "ID not found in WIN_ORDER ID series.";
+ err_line(!strlen(tmp_order) && tmp_active, err_null);
+ err_line(!strchr(tmp_order, tmp_active), err_outside);
+ }
+ else if (!strcmp(token0, "KEY"))
+ {
+ err_line(NULL != token_from_line(NULL), "Too many values.");
+ if (* win_flags & EDIT_STARTED)
+ {
+ set_keybindings(token1, token2, * win_flags, &win->kb);
+ }
+ else
+ {
+ set_keybindings(token1, token2, kbd_flags, kbdb);
+ }
+ }
+ else
+ {
+ return 0;
+ }
+ return 1;
+}
+
+
+
+static void set_keybindings(char * token1, char * token2, uint8_t flags,
+ struct KeyBindingDB * kbdb)
+{
+ char * err_out = "Outside appropriate definition's context.";
+ char * err_code = "Invalid keycode. Must be >= 0 and < 1000.";
+ err_line(!(flags & EDIT_STARTED), err_out);
+ err_line(!token2, "No binding to key given.");
+ char * err_many = "No more than 255 keybindings allowed in one section.";
+ err_line(UINT8_MAX == kbdb->n_of_kbs, err_many);
+ struct KeyBinding kb;
+ uint8_t test = strlen(token1);
+ err_line(!test, err_code);
+ uint8_t i;
+ for (i = 0; '\0' != token1[i]; i++)
+ {
+ test= i > 2 || '0' > token1[i] || '9' < token1[i];
+ err_line(test, err_code);
+ }
+ kb.keycode = atoi(token1);
+ char * err_uniq = "Binding to key already defined.";
+ err_line(NULL != get_command_to_keycode(kbdb, kb.keycode), err_uniq);
+ kb.command = get_command(token2);
+ err_line(!(kb.command), "No such command in command DB.");
+ array_append(kbdb->n_of_kbs, sizeof(struct KeyBinding), (void *) &kb,
+ (void **) kbdb);
+ kbdb->n_of_kbs++;
+}
+
+
+