+static inline int get_user_key_map_idx(int key)
+{
+ assert(!is_internal_key(key));
+ return key - NUM_INTERNALLY_MAPPED_KEYS;
+}
+
+static inline int get_key_map_idx(int key)
+{
+ return is_internal_key(key)?
+ get_internal_key_map_idx(key) : get_user_key_map_idx(key);
+}
+
+static inline const char *get_user_key_map_arg(int key)
+{
+ return lls_string_val(get_user_key_map_idx(key), OPT_RESULT(KEY_MAP));
+}
+
+static inline char *get_internal_key_map_seq(int key)
+{
+ return para_strdup(default_keyseqs[get_internal_key_map_idx(key)]);
+}
+
+static char *get_user_key_map_seq(int key)
+{
+ const char *kma = get_user_key_map_arg(key);
+ const char *p = strchr(kma + 1, ':');
+ char *result;
+ int len;
+
+ if (!p)
+ return NULL;
+ len = p - kma;
+ result = para_malloc(len + 1);
+ memcpy(result, kma, len);
+ result[len] = '\0';
+ return result;
+}
+
+static char *get_key_map_seq(int key)
+{
+ return is_internal_key(key)?
+ get_internal_key_map_seq(key) : get_user_key_map_seq(key);
+}
+
+static char *get_key_map_seq_safe(int key)
+{
+ const char hex[] = "0123456789abcdef";
+ char *seq = get_key_map_seq(key), *sseq;
+ size_t n, len = strlen(seq);
+
+ if (len == 1 && isprint(*seq))
+ return seq;
+ sseq = para_malloc(2 + 2 * len + 1);
+ sseq[0] = '0';
+ sseq[1] = 'x';
+ for (n = 0; n < len; n++) {
+ uint8_t val = (seq[n] & 0xf0) >> 4;
+ sseq[2 + 2 * n] = hex[val];
+ val = seq[n] & 0xf;
+ sseq[2 + 2 * n + 1] = hex[val];
+ }
+ free(seq);
+ sseq[2 + 2 * n] = '\0';
+ return sseq;
+}
+
+static inline char *get_internal_key_map_cmd(int key)
+{
+ return para_strdup(default_commands[get_internal_key_map_idx(key)]);
+}
+
+static char *get_user_key_map_cmd(int key)
+{
+ const char *kma = get_user_key_map_arg(key);
+ const char *p = strchr(kma + 1, ':');
+
+ if (!p)
+ return NULL;
+ return para_strdup(p + 1);
+}
+
+static char *get_key_map_cmd(int key)
+{
+ return is_internal_key(key)?
+ get_internal_key_map_cmd(key) : get_user_key_map_cmd(key);
+}
+
+static char **get_mapped_keyseqs(void)
+{
+ char **result;
+ int i;
+
+ result = para_malloc((NUM_MAPPED_KEYS + 1) * sizeof(char *));
+ FOR_EACH_MAPPED_KEY(i) {
+ char *seq = get_key_map_seq(i);
+ result[i] = seq;
+ }
+ result[i] = NULL;
+ return result;
+}
+
+static struct i9e_completer pp_completers[];
+
+I9E_DUMMY_COMPLETER(jmp);
+I9E_DUMMY_COMPLETER(next);
+I9E_DUMMY_COMPLETER(prev);
+I9E_DUMMY_COMPLETER(fg);
+I9E_DUMMY_COMPLETER(bg);
+I9E_DUMMY_COMPLETER(ls);
+I9E_DUMMY_COMPLETER(info);
+I9E_DUMMY_COMPLETER(play);
+I9E_DUMMY_COMPLETER(pause);
+I9E_DUMMY_COMPLETER(tasks);
+I9E_DUMMY_COMPLETER(quit);
+I9E_DUMMY_COMPLETER(ff);
+
+static void help_completer(struct i9e_completion_info *ci,
+ struct i9e_completion_result *cr)
+{
+ char *opts[] = {LSG_PLAY_CMD_HELP_OPTS, NULL};
+
+ if (ci->word[0] == '-') {
+ i9e_complete_option(opts, ci, cr);
+ return;
+ }
+ cr->matches = i9e_complete_commands(ci->word, pp_completers);
+}
+
+static struct i9e_completer pp_completers[] = {
+#define LSG_PLAY_CMD_CMD(_name) {.name = #_name, \
+ .completer = _name ## _completer}
+ LSG_PLAY_CMD_SUBCOMMANDS
+#undef LSG_PLAY_CMD_CMD
+ {.name = NULL}
+};
+
+static void attach_stdout(const char *name)
+{
+ if (pt->btrn)
+ return;
+ pt->btrn = btr_new_node(&(struct btr_node_description)
+ EMBRACE(.name = name));
+ i9e_attach_to_stdout(pt->btrn);
+}
+
+static void detach_stdout(void)
+{
+ btr_remove_node(&pt->btrn);
+}
+
+static int com_quit(__a_unused struct lls_parse_result *lpr)
+{
+ pt->rq = CRT_TERM_RQ;
+ return 0;
+}
+EXPORT_PLAY_CMD_HANDLER(quit);
+
+static int com_help(struct lls_parse_result *lpr)
+{
+ int i;
+ char *buf;
+ size_t sz;
+ unsigned n;
+ const struct lls_opt_result *r =
+ lls_opt_result(LSG_PLAY_CMD_HELP_OPT_LONG, lpr);
+ bool long_help = lls_opt_given(r);
+
+ if (!pt->background) {
+ FOR_EACH_MAPPED_KEY(i) {
+ bool internal = is_internal_key(i);
+ int idx = get_key_map_idx(i);
+ char *seq = get_key_map_seq_safe(i);
+ char *kmc = get_key_map_cmd(i);
+ sz = xasprintf(&buf, "%s key #%d: %s -> %s\n",
+ internal? "internal" : "user-defined",
+ idx, seq, kmc);
+ btr_add_output(buf, sz, pt->btrn);
+ free(seq);
+ free(kmc);