/* vifm
 * Copyright (C) 2012 xaizek.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include "bracket_notation.h"

#include <curses.h>

#include <assert.h> /* assert() */
#include <ctype.h> /* tolower() */
#include <stddef.h> /* NULL size_t wchar_t */
#include <stdlib.h> /* free() */
#include <string.h> /* strcpy() strlen() */
#include <wchar.h> /* wcscpy() wcslen() */
#include <wctype.h> /* iswcntrl() */

#include "compat/curses.h"
#include "compat/reallocarray.h"
#include "engine/text_buffer.h"
#include "modes/wk.h"
#include "utils/macros.h"
#include "utils/str.h"
#include "utils/utils.h"

/* Bracket notation entry description structure. */
typedef struct
{
	const wchar_t *notation; /* The notation itself. */
	const wchar_t key[8]; /* The replacement for the notation. */
	size_t len; /* Length of the notation. */
}
key_pair_t;

static int notation_sorter(const void *first, const void *second);
static const key_pair_t * find_notation(const wchar_t str[]);
static const char * wchar_to_spec(const wchar_t c[], size_t *len, int bs);

/* All notation fields must be written in lower case. */
static key_pair_t key_pairs[] = {
	{ L"<esc>",      L"\x1b"              },
	{ L"<c-@>",      { WC_C_SPACE }       },
	{ L"<c-a>",      L"\x01"              },
	{ L"<c-b>",      L"\x02"              },
	{ L"<c-c>",      L"\x03"              },
	{ L"<c-d>",      L"\x04"              },
	{ L"<c-e>",      L"\x05"              },
	{ L"<c-f>",      L"\x06"              },
	{ L"<c-g>",      L"\x07"              },
	{ L"<c-h>",      L"\x08"              },
	{ L"<c-i>",      L"\x09"              },
	{ L"<c-j>",      L"\x0a"              },
	{ L"<c-k>",      L"\x0b"              },
	{ L"<c-l>",      L"\x0c"              },
	{ L"<c-m>",      L"\x0d"              },
	{ L"<c-n>",      L"\x0e"              },
	{ L"<c-o>",      L"\x0f"              },
	{ L"<c-p>",      L"\x10"              },
	{ L"<c-q>",      L"\x11"              },
	{ L"<c-r>",      L"\x12"              },
	{ L"<c-s>",      L"\x13"              },
	{ L"<c-t>",      L"\x14"              },
	{ L"<c-u>",      L"\x15"              },
	{ L"<c-v>",      L"\x16"              },
	{ L"<c-w>",      L"\x17"              },
	{ L"<c-x>",      L"\x18"              },
	{ L"<c-y>",      L"\x19"              },
	{ L"<c-z>",      L"\x1a"              },
	{ L"<c-[>",      L"\x1b"              },
	{ L"<c-\\>",     L"\x1c"              },
	{ L"<c-]>",      L"\x1d"              },
	{ L"<c-^>",      L"\x1e"              },
	{ L"<c-_>",      L"\x1f"              },
	{ L"<s-c-a>",    L"\x01"              },
	{ L"<s-c-b>",    L"\x02"              },
	{ L"<s-c-c>",    L"\x03"              },
	{ L"<s-c-d>",    L"\x04"              },
	{ L"<s-c-e>",    L"\x05"              },
	{ L"<s-c-f>",    L"\x06"              },
	{ L"<s-c-g>",    L"\x07"              },
	{ L"<s-c-h>",    L"\x08"              },
	{ L"<s-c-i>",    L"\x09"              },
	{ L"<s-c-j>",    L"\x0a"              },
	{ L"<s-c-k>",    L"\x0b"              },
	{ L"<s-c-l>",    L"\x0c"              },
	{ L"<s-c-m>",    L"\x0d"              },
	{ L"<s-c-n>",    L"\x0e"              },
	{ L"<s-c-o>",    L"\x0f"              },
	{ L"<s-c-p>",    L"\x10"              },
	{ L"<s-c-q>",    L"\x11"              },
	{ L"<s-c-r>",    L"\x12"              },
	{ L"<s-c-s>",    L"\x13"              },
	{ L"<s-c-t>",    L"\x14"              },
	{ L"<s-c-u>",    L"\x15"              },
	{ L"<s-c-v>",    L"\x16"              },
	{ L"<s-c-w>",    L"\x17"              },
	{ L"<s-c-x>",    L"\x18"              },
	{ L"<s-c-y>",    L"\x19"              },
	{ L"<s-c-z>",    L"\x1a"              },
	{ L"<s-c-[>",    L"\x1b"              },
	{ L"<s-c-\\>",   L"\x1c"              },
	{ L"<s-c-]>",    L"\x1d"              },
	{ L"<s-c-^>",    L"\x1e"              },
	{ L"<s-c-_>",    L"\x1f"              },
	{ L"<c-s-a>",    L"\x01"              },
	{ L"<c-s-b>",    L"\x02"              },
	{ L"<c-s-c>",    L"\x03"              },
	{ L"<c-s-d>",    L"\x04"              },
	{ L"<c-s-e>",    L"\x05"              },
	{ L"<c-s-f>",    L"\x06"              },
	{ L"<c-s-g>",    L"\x07"              },
	{ L"<c-s-h>",    L"\x08"              },
	{ L"<c-s-i>",    L"\x09"              },
	{ L"<c-s-j>",    L"\x0a"              },
	{ L"<c-s-k>",    L"\x0b"              },
	{ L"<c-s-l>",    L"\x0c"              },
	{ L"<c-s-m>",    L"\x0d"              },
	{ L"<c-s-n>",    L"\x0e"              },
	{ L"<c-s-o>",    L"\x0f"              },
	{ L"<c-s-p>",    L"\x10"              },
	{ L"<c-s-q>",    L"\x11"              },
	{ L"<c-s-r>",    L"\x12"              },
	{ L"<c-s-s>",    L"\x13"              },
	{ L"<c-s-t>",    L"\x14"              },
	{ L"<c-s-u>",    L"\x15"              },
	{ L"<c-s-v>",    L"\x16"              },
	{ L"<c-s-w>",    L"\x17"              },
	{ L"<c-s-x>",    L"\x18"              },
	{ L"<c-s-y>",    L"\x19"              },
	{ L"<c-s-z>",    L"\x1a"              },
	{ L"<c-s-[>",    L"\x1b"              },
	{ L"<c-s-\\>",   L"\x1c"              },
	{ L"<c-s-]>",    L"\x1d"              },
	{ L"<c-s-^>",    L"\x1e"              },
	{ L"<c-s-_>",    L"\x1f"              },
	{ L"<cr>",       L"\r"                },
	{ L"<space>",    L" "                 },
	{ L"<nop>",      L""                  },
	{ L"<tab>",      L"\t"                },
	{ L"<lt>",       L"<"                 },
#ifdef ENABLE_EXTENDED_KEYS
	{ L"<s-tab>",    { K(KEY_BTAB) }      },
#else
	{ L"<s-tab>",    L"\033"L"[Z"         },
#endif
#ifndef __PDCURSES__
	{ L"<a-0>",      L"\033"L"0"          },
	{ L"<a-1>",      L"\033"L"1"          },
	{ L"<a-2>",      L"\033"L"2"          },
	{ L"<a-3>",      L"\033"L"3"          },
	{ L"<a-4>",      L"\033"L"4"          },
	{ L"<a-5>",      L"\033"L"5"          },
	{ L"<a-6>",      L"\033"L"6"          },
	{ L"<a-7>",      L"\033"L"7"          },
	{ L"<a-8>",      L"\033"L"8"          },
	{ L"<a-9>",      L"\033"L"9"          },
	{ L"<a-a>",      L"\033"L"a"          },
	{ L"<a-b>",      L"\033"L"b"          },
	{ L"<a-c>",      L"\033"L"c"          },
	{ L"<a-d>",      L"\033"L"d"          },
	{ L"<a-e>",      L"\033"L"e"          },
	{ L"<a-f>",      L"\033"L"f"          },
	{ L"<a-g>",      L"\033"L"g"          },
	{ L"<a-h>",      L"\033"L"h"          },
	{ L"<a-i>",      L"\033"L"i"          },
	{ L"<a-j>",      L"\033"L"j"          },
	{ L"<a-k>",      L"\033"L"k"          },
	{ L"<a-l>",      L"\033"L"l"          },
	{ L"<a-m>",      L"\033"L"m"          },
	{ L"<a-n>",      L"\033"L"n"          },
	{ L"<a-o>",      L"\033"L"o"          },
	{ L"<a-p>",      L"\033"L"p"          },
	{ L"<a-q>",      L"\033"L"q"          },
	{ L"<a-r>",      L"\033"L"r"          },
	{ L"<a-s>",      L"\033"L"s"          },
	{ L"<a-t>",      L"\033"L"t"          },
	{ L"<a-u>",      L"\033"L"u"          },
	{ L"<a-v>",      L"\033"L"v"          },
	{ L"<a-w>",      L"\033"L"w"          },
	{ L"<a-x>",      L"\033"L"x"          },
	{ L"<a-y>",      L"\033"L"y"          },
	{ L"<a-z>",      L"\033"L"z"          },
	{ L"<m-0>",      L"\033"L"0"          },
	{ L"<m-1>",      L"\033"L"1"          },
	{ L"<m-2>",      L"\033"L"2"          },
	{ L"<m-3>",      L"\033"L"3"          },
	{ L"<m-4>",      L"\033"L"4"          },
	{ L"<m-5>",      L"\033"L"5"          },
	{ L"<m-6>",      L"\033"L"6"          },
	{ L"<m-7>",      L"\033"L"7"          },
	{ L"<m-8>",      L"\033"L"8"          },
	{ L"<m-9>",      L"\033"L"9"          },
	{ L"<m-a>",      L"\033"L"a"          },
	{ L"<m-b>",      L"\033"L"b"          },
	{ L"<m-c>",      L"\033"L"c"          },
	{ L"<m-d>",      L"\033"L"d"          },
	{ L"<m-e>",      L"\033"L"e"          },
	{ L"<m-f>",      L"\033"L"f"          },
	{ L"<m-g>",      L"\033"L"g"          },
	{ L"<m-h>",      L"\033"L"h"          },
	{ L"<m-i>",      L"\033"L"i"          },
	{ L"<m-j>",      L"\033"L"j"          },
	{ L"<m-k>",      L"\033"L"k"          },
	{ L"<m-l>",      L"\033"L"l"          },
	{ L"<m-m>",      L"\033"L"m"          },
	{ L"<m-n>",      L"\033"L"n"          },
	{ L"<m-o>",      L"\033"L"o"          },
	{ L"<m-p>",      L"\033"L"p"          },
	{ L"<m-q>",      L"\033"L"q"          },
	{ L"<m-r>",      L"\033"L"r"          },
	{ L"<m-s>",      L"\033"L"s"          },
	{ L"<m-t>",      L"\033"L"t"          },
	{ L"<m-u>",      L"\033"L"u"          },
	{ L"<m-v>",      L"\033"L"v"          },
	{ L"<m-w>",      L"\033"L"w"          },
	{ L"<m-x>",      L"\033"L"x"          },
	{ L"<m-y>",      L"\033"L"y"          },
	{ L"<m-z>",      L"\033"L"z"          },
	{ L"<a-c-a>",    L"\033"L"\x01"       },
	{ L"<a-c-b>",    L"\033"L"\x02"       },
	{ L"<a-c-c>",    L"\033"L"\x03"       },
	{ L"<a-c-d>",    L"\033"L"\x04"       },
	{ L"<a-c-e>",    L"\033"L"\x05"       },
	{ L"<a-c-f>",    L"\033"L"\x06"       },
	{ L"<a-c-g>",    L"\033"L"\x07"       },
	{ L"<a-c-h>",    L"\033"L"\x08"       },
	{ L"<a-c-i>",    L"\033"L"\x09"       },
	{ L"<a-c-j>",    L"\033"L"\x0a"       },
	{ L"<a-c-k>",    L"\033"L"\x0b"       },
	{ L"<a-c-l>",    L"\033"L"\x0c"       },
	{ L"<a-c-m>",    L"\033"L"\x0d"       },
	{ L"<a-c-n>",    L"\033"L"\x0e"       },
	{ L"<a-c-o>",    L"\033"L"\x0f"       },
	{ L"<a-c-p>",    L"\033"L"\x10"       },
	{ L"<a-c-q>",    L"\033"L"\x11"       },
	{ L"<a-c-r>",    L"\033"L"\x12"       },
	{ L"<a-c-s>",    L"\033"L"\x13"       },
	{ L"<a-c-t>",    L"\033"L"\x14"       },
	{ L"<a-c-u>",    L"\033"L"\x15"       },
	{ L"<a-c-v>",    L"\033"L"\x16"       },
	{ L"<a-c-w>",    L"\033"L"\x17"       },
	{ L"<a-c-x>",    L"\033"L"\x18"       },
	{ L"<a-c-y>",    L"\033"L"\x19"       },
	{ L"<a-c-z>",    L"\033"L"\x1a"       },
	{ L"<m-c-a>",    L"\033"L"\x01"       },
	{ L"<m-c-b>",    L"\033"L"\x02"       },
	{ L"<m-c-c>",    L"\033"L"\x03"       },
	{ L"<m-c-d>",    L"\033"L"\x04"       },
	{ L"<m-c-e>",    L"\033"L"\x05"       },
	{ L"<m-c-f>",    L"\033"L"\x06"       },
	{ L"<m-c-g>",    L"\033"L"\x07"       },
	{ L"<m-c-h>",    L"\033"L"\x08"       },
	{ L"<m-c-i>",    L"\033"L"\x09"       },
	{ L"<m-c-j>",    L"\033"L"\x0a"       },
	{ L"<m-c-k>",    L"\033"L"\x0b"       },
	{ L"<m-c-l>",    L"\033"L"\x0c"       },
	{ L"<m-c-m>",    L"\033"L"\x0d"       },
	{ L"<m-c-n>",    L"\033"L"\x0e"       },
	{ L"<m-c-o>",    L"\033"L"\x0f"       },
	{ L"<m-c-p>",    L"\033"L"\x10"       },
	{ L"<m-c-q>",    L"\033"L"\x11"       },
	{ L"<m-c-r>",    L"\033"L"\x12"       },
	{ L"<m-c-s>",    L"\033"L"\x13"       },
	{ L"<m-c-t>",    L"\033"L"\x14"       },
	{ L"<m-c-u>",    L"\033"L"\x15"       },
	{ L"<m-c-v>",    L"\033"L"\x16"       },
	{ L"<m-c-w>",    L"\033"L"\x17"       },
	{ L"<m-c-x>",    L"\033"L"\x18"       },
	{ L"<m-c-y>",    L"\033"L"\x19"       },
	{ L"<m-c-z>",    L"\033"L"\x1a"       },
	{ L"<c-a-a>",    L"\033"L"\x01"       },
	{ L"<c-a-b>",    L"\033"L"\x02"       },
	{ L"<c-a-c>",    L"\033"L"\x03"       },
	{ L"<c-a-d>",    L"\033"L"\x04"       },
	{ L"<c-a-e>",    L"\033"L"\x05"       },
	{ L"<c-a-f>",    L"\033"L"\x06"       },
	{ L"<c-a-g>",    L"\033"L"\x07"       },
	{ L"<c-a-h>",    L"\033"L"\x08"       },
	{ L"<c-a-i>",    L"\033"L"\x09"       },
	{ L"<c-a-j>",    L"\033"L"\x0a"       },
	{ L"<c-a-k>",    L"\033"L"\x0b"       },
	{ L"<c-a-l>",    L"\033"L"\x0c"       },
	{ L"<c-a-m>",    L"\033"L"\x0d"       },
	{ L"<c-a-n>",    L"\033"L"\x0e"       },
	{ L"<c-a-o>",    L"\033"L"\x0f"       },
	{ L"<c-a-p>",    L"\033"L"\x10"       },
	{ L"<c-a-q>",    L"\033"L"\x11"       },
	{ L"<c-a-r>",    L"\033"L"\x12"       },
	{ L"<c-a-s>",    L"\033"L"\x13"       },
	{ L"<c-a-t>",    L"\033"L"\x14"       },
	{ L"<c-a-u>",    L"\033"L"\x15"       },
	{ L"<c-a-v>",    L"\033"L"\x16"       },
	{ L"<c-a-w>",    L"\033"L"\x17"       },
	{ L"<c-a-x>",    L"\033"L"\x18"       },
	{ L"<c-a-y>",    L"\033"L"\x19"       },
	{ L"<c-a-z>",    L"\033"L"\x1a"       },
	{ L"<c-m-a>",    L"\033"L"\x01"       },
	{ L"<c-m-b>",    L"\033"L"\x02"       },
	{ L"<c-m-c>",    L"\033"L"\x03"       },
	{ L"<c-m-d>",    L"\033"L"\x04"       },
	{ L"<c-m-e>",    L"\033"L"\x05"       },
	{ L"<c-m-f>",    L"\033"L"\x06"       },
	{ L"<c-m-g>",    L"\033"L"\x07"       },
	{ L"<c-m-h>",    L"\033"L"\x08"       },
	{ L"<c-m-i>",    L"\033"L"\x09"       },
	{ L"<c-m-j>",    L"\033"L"\x0a"       },
	{ L"<c-m-k>",    L"\033"L"\x0b"       },
	{ L"<c-m-l>",    L"\033"L"\x0c"       },
	{ L"<c-m-m>",    L"\033"L"\x0d"       },
	{ L"<c-m-n>",    L"\033"L"\x0e"       },
	{ L"<c-m-o>",    L"\033"L"\x0f"       },
	{ L"<c-m-p>",    L"\033"L"\x10"       },
	{ L"<c-m-q>",    L"\033"L"\x11"       },
	{ L"<c-m-r>",    L"\033"L"\x12"       },
	{ L"<c-m-s>",    L"\033"L"\x13"       },
	{ L"<c-m-t>",    L"\033"L"\x14"       },
	{ L"<c-m-u>",    L"\033"L"\x15"       },
	{ L"<c-m-v>",    L"\033"L"\x16"       },
	{ L"<c-m-w>",    L"\033"L"\x17"       },
	{ L"<c-m-x>",    L"\033"L"\x18"       },
	{ L"<c-m-y>",    L"\033"L"\x19"       },
	{ L"<c-m-z>",    L"\033"L"\x1a"       },
	{ L"<a-s-a>",    L"\033"L"A"          },
	{ L"<a-s-b>",    L"\033"L"B"          },
	{ L"<a-s-c>",    L"\033"L"C"          },
	{ L"<a-s-d>",    L"\033"L"D"          },
	{ L"<a-s-e>",    L"\033"L"E"          },
	{ L"<a-s-f>",    L"\033"L"F"          },
	{ L"<a-s-g>",    L"\033"L"G"          },
	{ L"<a-s-h>",    L"\033"L"H"          },
	{ L"<a-s-i>",    L"\033"L"I"          },
	{ L"<a-s-j>",    L"\033"L"J"          },
	{ L"<a-s-k>",    L"\033"L"K"          },
	{ L"<a-s-l>",    L"\033"L"L"          },
	{ L"<a-s-m>",    L"\033"L"M"          },
	{ L"<a-s-n>",    L"\033"L"N"          },
	{ L"<a-s-o>",    L"\033"L"O"          },
	{ L"<a-s-p>",    L"\033"L"P"          },
	{ L"<a-s-q>",    L"\033"L"Q"          },
	{ L"<a-s-r>",    L"\033"L"R"          },
	{ L"<a-s-s>",    L"\033"L"S"          },
	{ L"<a-s-t>",    L"\033"L"T"          },
	{ L"<a-s-u>",    L"\033"L"U"          },
	{ L"<a-s-v>",    L"\033"L"V"          },
	{ L"<a-s-w>",    L"\033"L"W"          },
	{ L"<a-s-x>",    L"\033"L"X"          },
	{ L"<a-s-y>",    L"\033"L"Y"          },
	{ L"<a-s-z>",    L"\033"L"Z"          },
	{ L"<s-a-a>",    L"\033"L"A"          },
	{ L"<s-a-b>",    L"\033"L"B"          },
	{ L"<s-a-c>",    L"\033"L"C"          },
	{ L"<s-a-d>",    L"\033"L"D"          },
	{ L"<s-a-e>",    L"\033"L"E"          },
	{ L"<s-a-f>",    L"\033"L"F"          },
	{ L"<s-a-g>",    L"\033"L"G"          },
	{ L"<s-a-h>",    L"\033"L"H"          },
	{ L"<s-a-i>",    L"\033"L"I"          },
	{ L"<s-a-j>",    L"\033"L"J"          },
	{ L"<s-a-k>",    L"\033"L"K"          },
	{ L"<s-a-l>",    L"\033"L"L"          },
	{ L"<s-a-m>",    L"\033"L"M"          },
	{ L"<s-a-n>",    L"\033"L"N"          },
	{ L"<s-a-o>",    L"\033"L"O"          },
	{ L"<s-a-p>",    L"\033"L"P"          },
	{ L"<s-a-q>",    L"\033"L"Q"          },
	{ L"<s-a-r>",    L"\033"L"R"          },
	{ L"<s-a-s>",    L"\033"L"S"          },
	{ L"<s-a-t>",    L"\033"L"T"          },
	{ L"<s-a-u>",    L"\033"L"U"          },
	{ L"<s-a-v>",    L"\033"L"V"          },
	{ L"<s-a-w>",    L"\033"L"W"          },
	{ L"<s-a-x>",    L"\033"L"X"          },
	{ L"<s-a-y>",    L"\033"L"Y"          },
	{ L"<s-a-z>",    L"\033"L"Z"          },
	{ L"<m-s-a>",    L"\033"L"A"          },
	{ L"<m-s-b>",    L"\033"L"B"          },
	{ L"<m-s-c>",    L"\033"L"C"          },
	{ L"<m-s-d>",    L"\033"L"D"          },
	{ L"<m-s-e>",    L"\033"L"E"          },
	{ L"<m-s-f>",    L"\033"L"F"          },
	{ L"<m-s-g>",    L"\033"L"G"          },
	{ L"<m-s-h>",    L"\033"L"H"          },
	{ L"<m-s-i>",    L"\033"L"I"          },
	{ L"<m-s-j>",    L"\033"L"J"          },
	{ L"<m-s-k>",    L"\033"L"K"          },
	{ L"<m-s-l>",    L"\033"L"L"          },
	{ L"<m-s-m>",    L"\033"L"M"          },
	{ L"<m-s-n>",    L"\033"L"N"          },
	{ L"<m-s-o>",    L"\033"L"O"          },
	{ L"<m-s-p>",    L"\033"L"P"          },
	{ L"<m-s-q>",    L"\033"L"Q"          },
	{ L"<m-s-r>",    L"\033"L"R"          },
	{ L"<m-s-s>",    L"\033"L"S"          },
	{ L"<m-s-t>",    L"\033"L"T"          },
	{ L"<m-s-u>",    L"\033"L"U"          },
	{ L"<m-s-v>",    L"\033"L"V"          },
	{ L"<m-s-w>",    L"\033"L"W"          },
	{ L"<m-s-x>",    L"\033"L"X"          },
	{ L"<m-s-y>",    L"\033"L"Y"          },
	{ L"<m-s-z>",    L"\033"L"Z"          },
	{ L"<s-m-a>",    L"\033"L"A"          },
	{ L"<s-m-b>",    L"\033"L"B"          },
	{ L"<s-m-c>",    L"\033"L"C"          },
	{ L"<s-m-d>",    L"\033"L"D"          },
	{ L"<s-m-e>",    L"\033"L"E"          },
	{ L"<s-m-f>",    L"\033"L"F"          },
	{ L"<s-m-g>",    L"\033"L"G"          },
	{ L"<s-m-h>",    L"\033"L"H"          },
	{ L"<s-m-i>",    L"\033"L"I"          },
	{ L"<s-m-j>",    L"\033"L"J"          },
	{ L"<s-m-k>",    L"\033"L"K"          },
	{ L"<s-m-l>",    L"\033"L"L"          },
	{ L"<s-m-m>",    L"\033"L"M"          },
	{ L"<s-m-n>",    L"\033"L"N"          },
	{ L"<s-m-o>",    L"\033"L"O"          },
	{ L"<s-m-p>",    L"\033"L"P"          },
	{ L"<s-m-q>",    L"\033"L"Q"          },
	{ L"<s-m-r>",    L"\033"L"R"          },
	{ L"<s-m-s>",    L"\033"L"S"          },
	{ L"<s-m-t>",    L"\033"L"T"          },
	{ L"<s-m-u>",    L"\033"L"U"          },
	{ L"<s-m-v>",    L"\033"L"V"          },
	{ L"<s-m-w>",    L"\033"L"W"          },
	{ L"<s-m-x>",    L"\033"L"X"          },
	{ L"<s-m-y>",    L"\033"L"Y"          },
	{ L"<s-m-z>",    L"\033"L"Z"          },
#else
	{ L"<a-a>",      { K(ALT_A) }         },
	{ L"<a-b>",      { K(ALT_B) }         },
	{ L"<a-c>",      { K(ALT_C) }         },
	{ L"<a-d>",      { K(ALT_D) }         },
	{ L"<a-e>",      { K(ALT_E) }         },
	{ L"<a-f>",      { K(ALT_F) }         },
	{ L"<a-g>",      { K(ALT_G) }         },
	{ L"<a-h>",      { K(ALT_H) }         },
	{ L"<a-i>",      { K(ALT_I) }         },
	{ L"<a-j>",      { K(ALT_J) }         },
	{ L"<a-k>",      { K(ALT_K) }         },
	{ L"<a-l>",      { K(ALT_L) }         },
	{ L"<a-m>",      { K(ALT_M) }         },
	{ L"<a-n>",      { K(ALT_N) }         },
	{ L"<a-o>",      { K(ALT_O) }         },
	{ L"<a-p>",      { K(ALT_P) }         },
	{ L"<a-q>",      { K(ALT_Q) }         },
	{ L"<a-r>",      { K(ALT_R) }         },
	{ L"<a-s>",      { K(ALT_S) }         },
	{ L"<a-t>",      { K(ALT_T) }         },
	{ L"<a-u>",      { K(ALT_U) }         },
	{ L"<a-v>",      { K(ALT_V) }         },
	{ L"<a-w>",      { K(ALT_W) }         },
	{ L"<a-x>",      { K(ALT_X) }         },
	{ L"<a-y>",      { K(ALT_Y) }         },
	{ L"<a-z>",      { K(ALT_Z) }         },
	{ L"<m-a>",      { K(ALT_A) }         },
	{ L"<m-b>",      { K(ALT_B) }         },
	{ L"<m-c>",      { K(ALT_C) }         },
	{ L"<m-d>",      { K(ALT_D) }         },
	{ L"<m-e>",      { K(ALT_E) }         },
	{ L"<m-f>",      { K(ALT_F) }         },
	{ L"<m-g>",      { K(ALT_G) }         },
	{ L"<m-h>",      { K(ALT_H) }         },
	{ L"<m-i>",      { K(ALT_I) }         },
	{ L"<m-j>",      { K(ALT_J) }         },
	{ L"<m-k>",      { K(ALT_K) }         },
	{ L"<m-l>",      { K(ALT_L) }         },
	{ L"<m-m>",      { K(ALT_M) }         },
	{ L"<m-n>",      { K(ALT_N) }         },
	{ L"<m-o>",      { K(ALT_O) }         },
	{ L"<m-p>",      { K(ALT_P) }         },
	{ L"<m-q>",      { K(ALT_Q) }         },
	{ L"<m-r>",      { K(ALT_R) }         },
	{ L"<m-s>",      { K(ALT_S) }         },
	{ L"<m-t>",      { K(ALT_T) }         },
	{ L"<m-u>",      { K(ALT_U) }         },
	{ L"<m-v>",      { K(ALT_V) }         },
	{ L"<m-w>",      { K(ALT_W) }         },
	{ L"<m-x>",      { K(ALT_X) }         },
	{ L"<m-y>",      { K(ALT_Y) }         },
	{ L"<m-z>",      { K(ALT_Z) }         },
#endif
	{ L"<del>",      L"\177"              },
#ifdef ENABLE_EXTENDED_KEYS
	{ L"<home>",     { K(KEY_HOME) }      },
	{ L"<end>",      { K(KEY_END) }       },
	{ L"<left>",     { K(KEY_LEFT) }      },
	{ L"<right>",    { K(KEY_RIGHT) }     },
	{ L"<up>",       { K(KEY_UP) }        },
	{ L"<down>",     { K(KEY_DOWN) }      },
	{ L"<bs>",       { K(KEY_BACKSPACE) } },
	{ L"<delete>",   { K(KEY_DC) }        },
	{ L"<insert>",   { K(KEY_IC) }        },
	{ L"<pageup>",   { K(KEY_PPAGE) }     },
	{ L"<pagedown>", { K(KEY_NPAGE) }     },
	{ L"<s-home>",   { K(KEY_SHOME) }     },
	{ L"<s-end>",    { K(KEY_SEND) }      },
	{ L"<s-left>",   { K(KEY_SLEFT) }     },
	{ L"<s-right>",  { K(KEY_SRIGHT) }    },
	{ L"<s-up>",     { K(KEY_SR) }        },
	{ L"<s-down>",   { K(KEY_SF) }        },
	{ L"<s-delete>", { K(KEY_SDC) }       },
	{ L"<s-insert>", { K(KEY_SIC) }       },
	{ L"<s-pageup>", { K(KEY_SPREVIOUS) } },
	{ L"<s-pagedown>", { K(KEY_SNEXT) }   },
	{ L"<f0>",       { K(KEY_F(0)) }      },
	{ L"<f1>",       { K(KEY_F(1)) }      },
	{ L"<f2>",       { K(KEY_F(2)) }      },
	{ L"<f3>",       { K(KEY_F(3)) }      },
	{ L"<f4>",       { K(KEY_F(4)) }      },
	{ L"<f5>",       { K(KEY_F(5)) }      },
	{ L"<f6>",       { K(KEY_F(6)) }      },
	{ L"<f7>",       { K(KEY_F(7)) }      },
	{ L"<f8>",       { K(KEY_F(8)) }      },
	{ L"<f9>",       { K(KEY_F(9)) }      },
	{ L"<f10>",      { K(KEY_F(10)) }     },
	{ L"<f11>",      { K(KEY_F(11)) }     },
	{ L"<f12>",      { K(KEY_F(12)) }     },
	{ L"<f13>",      { K(KEY_F(13)) }     },
	{ L"<f14>",      { K(KEY_F(14)) }     },
	{ L"<f15>",      { K(KEY_F(15)) }     },
	{ L"<f16>",      { K(KEY_F(16)) }     },
	{ L"<f17>",      { K(KEY_F(17)) }     },
	{ L"<f18>",      { K(KEY_F(18)) }     },
	{ L"<f19>",      { K(KEY_F(19)) }     },
	{ L"<f20>",      { K(KEY_F(20)) }     },
	{ L"<f21>",      { K(KEY_F(21)) }     },
	{ L"<f22>",      { K(KEY_F(22)) }     },
	{ L"<f23>",      { K(KEY_F(23)) }     },
	{ L"<f24>",      { K(KEY_F(24)) }     },
	{ L"<f25>",      { K(KEY_F(25)) }     },
	{ L"<f26>",      { K(KEY_F(26)) }     },
	{ L"<f27>",      { K(KEY_F(27)) }     },
	{ L"<f28>",      { K(KEY_F(28)) }     },
	{ L"<f29>",      { K(KEY_F(29)) }     },
	{ L"<f30>",      { K(KEY_F(30)) }     },
	{ L"<f31>",      { K(KEY_F(31)) }     },
	{ L"<f32>",      { K(KEY_F(32)) }     },
	{ L"<f33>",      { K(KEY_F(33)) }     },
	{ L"<f34>",      { K(KEY_F(34)) }     },
	{ L"<f35>",      { K(KEY_F(35)) }     },
	{ L"<f36>",      { K(KEY_F(36)) }     },
	{ L"<f37>",      { K(KEY_F(37)) }     },
	{ L"<f38>",      { K(KEY_F(38)) }     },
	{ L"<f39>",      { K(KEY_F(39)) }     },
	{ L"<f40>",      { K(KEY_F(40)) }     },
	{ L"<f41>",      { K(KEY_F(41)) }     },
	{ L"<f42>",      { K(KEY_F(42)) }     },
	{ L"<f43>",      { K(KEY_F(43)) }     },
	{ L"<f44>",      { K(KEY_F(44)) }     },
	{ L"<f45>",      { K(KEY_F(45)) }     },
	{ L"<f46>",      { K(KEY_F(46)) }     },
	{ L"<f47>",      { K(KEY_F(47)) }     },
	{ L"<f48>",      { K(KEY_F(48)) }     },
	{ L"<f49>",      { K(KEY_F(49)) }     },
	{ L"<f50>",      { K(KEY_F(50)) }     },
	{ L"<f51>",      { K(KEY_F(51)) }     },
	{ L"<f52>",      { K(KEY_F(52)) }     },
	{ L"<f53>",      { K(KEY_F(53)) }     },
	{ L"<f54>",      { K(KEY_F(54)) }     },
	{ L"<f55>",      { K(KEY_F(55)) }     },
	{ L"<f56>",      { K(KEY_F(56)) }     },
	{ L"<f57>",      { K(KEY_F(57)) }     },
	{ L"<f58>",      { K(KEY_F(58)) }     },
	{ L"<f59>",      { K(KEY_F(59)) }     },
	{ L"<f60>",      { K(KEY_F(60)) }     },
	{ L"<f61>",      { K(KEY_F(61)) }     },
	{ L"<f62>",      { K(KEY_F(62)) }     },
	{ L"<f63>",      { K(KEY_F(63)) }     },
	{ L"<s-f1>",     { K(KEY_F(13)) }     },
	{ L"<s-f2>",     { K(KEY_F(14)) }     },
	{ L"<s-f3>",     { K(KEY_F(15)) }     },
	{ L"<s-f4>",     { K(KEY_F(16)) }     },
	{ L"<s-f5>",     { K(KEY_F(17)) }     },
	{ L"<s-f6>",     { K(KEY_F(18)) }     },
	{ L"<s-f7>",     { K(KEY_F(19)) }     },
	{ L"<s-f8>",     { K(KEY_F(20)) }     },
	{ L"<s-f9>",     { K(KEY_F(21)) }     },
	{ L"<s-f10>",    { K(KEY_F(22)) }     },
	{ L"<s-f11>",    { K(KEY_F(23)) }     },
	{ L"<s-f12>",    { K(KEY_F(24)) }     },
	{ L"<c-f1>",     { K(KEY_F(25)) }     },
	{ L"<c-f2>",     { K(KEY_F(26)) }     },
	{ L"<c-f3>",     { K(KEY_F(27)) }     },
	{ L"<c-f4>",     { K(KEY_F(28)) }     },
	{ L"<c-f5>",     { K(KEY_F(29)) }     },
	{ L"<c-f6>",     { K(KEY_F(30)) }     },
	{ L"<c-f7>",     { K(KEY_F(31)) }     },
	{ L"<c-f8>",     { K(KEY_F(32)) }     },
	{ L"<c-f9>",     { K(KEY_F(33)) }     },
	{ L"<c-f10>",    { K(KEY_F(34)) }     },
	{ L"<c-f11>",    { K(KEY_F(35)) }     },
	{ L"<c-f12>",    { K(KEY_F(36)) }     },
	{ L"<a-f1>",     { K(KEY_F(37)) }     },
	{ L"<a-f2>",     { K(KEY_F(38)) }     },
	{ L"<a-f3>",     { K(KEY_F(39)) }     },
	{ L"<a-f4>",     { K(KEY_F(40)) }     },
	{ L"<a-f5>",     { K(KEY_F(41)) }     },
	{ L"<a-f6>",     { K(KEY_F(42)) }     },
	{ L"<a-f7>",     { K(KEY_F(43)) }     },
	{ L"<a-f8>",     { K(KEY_F(44)) }     },
	{ L"<a-f9>",     { K(KEY_F(45)) }     },
	{ L"<a-f10>",    { K(KEY_F(46)) }     },
	{ L"<a-f11>",    { K(KEY_F(47)) }     },
	{ L"<a-f12>",    { K(KEY_F(48)) }     },
	{ L"<m-f1>",     { K(KEY_F(37)) }     },
	{ L"<m-f2>",     { K(KEY_F(38)) }     },
	{ L"<m-f3>",     { K(KEY_F(39)) }     },
	{ L"<m-f4>",     { K(KEY_F(40)) }     },
	{ L"<m-f5>",     { K(KEY_F(41)) }     },
	{ L"<m-f6>",     { K(KEY_F(42)) }     },
	{ L"<m-f7>",     { K(KEY_F(43)) }     },
	{ L"<m-f8>",     { K(KEY_F(44)) }     },
	{ L"<m-f9>",     { K(KEY_F(45)) }     },
	{ L"<m-f10>",    { K(KEY_F(46)) }     },
	{ L"<m-f11>",    { K(KEY_F(47)) }     },
	{ L"<m-f12>",    { K(KEY_F(48)) }     },
#endif
};

void
init_bracket_notation(void)
{
	size_t i;
	for(i = 0; i < ARRAY_LEN(key_pairs); i++)
	{
		key_pairs[i].len = wcslen(key_pairs[i].notation);
	}

	safe_qsort(key_pairs, ARRAY_LEN(key_pairs), sizeof(key_pairs[0]),
			notation_sorter);
}

/* Sorter function to be called by qsort. */
static int
notation_sorter(const void *first, const void *second)
{
	const key_pair_t *paira = (const key_pair_t *)first;
	const key_pair_t *pairb = (const key_pair_t *)second;
	const wchar_t *stra = paira->notation;
	const wchar_t *strb = pairb->notation;
	return wcscmp(stra, strb);
}

wchar_t *
substitute_specs(const char cmd[])
{
	wchar_t *result;

	wchar_t *const cmdw = to_wide(cmd);
	if(cmdw == NULL)
	{
		return NULL;
	}

	result = substitute_specsw(cmdw);
	free(cmdw);

	return result;
}

wchar_t *
substitute_specsw(const wchar_t cmd[])
{
	wchar_t *buf, *p;
	const size_t len = wcslen(cmd) + 1;
	const wchar_t *s = cmd;

	buf = reallocarray(NULL, len, sizeof(wchar_t));
	if(buf == NULL)
	{
		return NULL;
	}

	p = buf;
	while(*s != L'\0')
	{
		const key_pair_t *const pair = find_notation(s);
		if(pair == NULL)
		{
			*p++ = *s++;
		}
		else
		{
			wcscpy(p, pair->key);
			p += wcslen(p);
			s += pair->len;
		}
	}
	*p = L'\0';
	assert((size_t)(p + 1 - buf) <= len && "Destination buffer was too small.");

	return buf;
}

const wchar_t *
spec_to_wstr(const char str[])
{
	wchar_t wide[strlen(str) + 1];

	int i;
	for(i = 0; str[i] != '\0'; ++i)
	{
		/* The notation uses Latin characters only. */
		wide[i] = (unsigned char)str[i];
	}
	wide[i] = L'\0';

	const key_pair_t *pair = find_notation(wide);
	if(pair == NULL)
	{
		return NULL;
	}

	return pair->key;
}

/* Performs binary search in the list of bracket notations.  Returns NULL if
 * str wasn't found in the list. */
static const key_pair_t *
find_notation(const wchar_t str[])
{
	wchar_t str_lowered[wcslen(str) + 1];
	int l = 0, u = ARRAY_LEN(key_pairs) - 1;

	wcscpy(str_lowered, str);
	wcstolower(str_lowered);

	while(l <= u)
	{
		const int i = l + (u - l)/2;
		const key_pair_t *const key_pair = &key_pairs[i];
		const int comp = wcsncmp(str_lowered, key_pair->notation, key_pair->len);
		if(comp == 0)
		{
			return key_pair;
		}
		else if(comp < 0)
		{
			u = i - 1;
		}
		else
		{
			l = i + 1;
		}
	}
	return NULL;
}

char *
wstr_to_spec(const wchar_t str[])
{
	vle_textbuf *const descr = vle_tb_create();

	const size_t str_len = wcslen(str);
	size_t seq_len;
	size_t i;

	for(i = 0U; i < str_len; i += seq_len)
	{
		if(str[i] == L' ' && i != 0U && i != str_len - 1)
		{
			vle_tb_append(descr, " ");
			seq_len = 1U;
		}
		else
		{
			vle_tb_append(descr, wchar_to_spec(&str[i], &seq_len, (i == 0U)));
		}
	}

	return vle_tb_release(descr);
}

/* Converts unicode character(s) starting at c into string form representing
 * corresponding key.  Upon exit *len is set to number of used characters from
 * the string.  Returns pointer to internal buffer. */
static const char *
wchar_to_spec(const wchar_t c[], size_t *len, int bs)
{
	/* TODO: refactor this function wchar_to_spec() */

	static char buf[256];

	*len = 1;
	switch(*c)
	{
		case L' ':             strcpy(buf, "<space>");      break;
		case L'\r':            strcpy(buf, "<cr>");         break;
		case L'\n':            strcpy(buf, "<c-j>");        break;
		case L'\177':          strcpy(buf, "<del>");        break;
		case L'<':             strcpy(buf, "<lt>");         break;
		case K(KEY_HOME):      strcpy(buf, "<home>");       break;
		case K(KEY_END):       strcpy(buf, "<end>");        break;
		case K(KEY_UP):        strcpy(buf, "<up>");         break;
		case K(KEY_DOWN):      strcpy(buf, "<down>");       break;
		case K(KEY_LEFT):      strcpy(buf, "<left>");       break;
		case K(KEY_RIGHT):     strcpy(buf, "<right>");      break;
		case K(KEY_DC):        strcpy(buf, "<delete>");     break;
		case K(KEY_BTAB):      strcpy(buf, "<s-tab>");      break;
		case K(KEY_PPAGE):     strcpy(buf, "<pageup>");     break;
		case K(KEY_NPAGE):     strcpy(buf, "<pagedown>");   break;
		case K(KEY_SHOME):     strcpy(buf, "<s-home>");     break;
		case K(KEY_SEND):      strcpy(buf, "<s-end>");      break;
		case K(KEY_SR):        strcpy(buf, "<s-up>");       break;
		case K(KEY_SF):        strcpy(buf, "<s-down>");     break;
		case K(KEY_SLEFT):     strcpy(buf, "<s-left>");     break;
		case K(KEY_SRIGHT):    strcpy(buf, "<s-right>");    break;
		case K(KEY_SDC):       strcpy(buf, "<s-delete>");   break;
		case K(KEY_SIC):       strcpy(buf, "<s-insert>");   break;
		case K(KEY_SPREVIOUS): strcpy(buf, "<s-pageup>");   break;
		case K(KEY_SNEXT):     strcpy(buf, "<s-pagedown>"); break;
		case WC_C_SPACE:       strcpy(buf, "<c-@>");        break;

		case L'\b':
			if(!bs)
			{
				goto def;
			}
			/* Fall through. */
		case K(KEY_BACKSPACE):
			strcpy(buf, "<bs>");
			break;

		case L'\033':
			if(c[1] == L'[' && c[2] == 'Z')
			{
				strcpy(buf, "<s-tab>");
				*len += 2;
				break;
			}
			if(c[1] != L'\0' && c[1] != L'\033')
			{
				if(isupper(c[1]))
				{
					strcpy(buf, "<a-s-a>");
					buf[5] += c[1] - L'A';
				}
				else
				{
					strcpy(buf, "<a-a>");
					buf[3] += c[1] - L'a';
				}
				++*len;
				break;
			}
			strcpy(buf, "<esc>");
			break;

		default:
		def:
			if(*c == '\n' || (*c > L' ' && *c < 256))
			{
				buf[0] = *c;
				buf[1] = '\0';
			}
			else if(*c >= K(KEY_F0) && *c < K(KEY_F0) + 10)
			{
				strcpy(buf, "<f0>");
				buf[2] += *c - K(KEY_F0);
			}
			else if(*c >= K(KEY_F0) + 13 && *c <= K(KEY_F0) + 21)
			{
				strcpy(buf, "<s-f1>");
				buf[4] += *c - (K(KEY_F0) + 13);
			}
			else if(*c >= K(KEY_F0) + 22 && *c <= K(KEY_F0) + 24)
			{
				strcpy(buf, "<s-f10>");
				buf[5] += *c - (K(KEY_F0) + 22);
			}
			else if(*c >= K(KEY_F0) + 25 && *c <= K(KEY_F0) + 33)
			{
				strcpy(buf, "<c-f1>");
				buf[4] += *c - (K(KEY_F0) + 25);
			}
			else if(*c >= K(KEY_F0) + 34 && *c <= K(KEY_F0) + 36)
			{
				strcpy(buf, "<c-f10>");
				buf[5] += *c - (K(KEY_F0) + 34);
			}
			else if(*c >= K(KEY_F0) + 37 && *c <= K(KEY_F0) + 45)
			{
				strcpy(buf, "<a-f1>");
				buf[4] += *c - (K(KEY_F0) + 37);
			}
			else if(*c >= K(KEY_F0) + 46 && *c <= K(KEY_F0) + 48)
			{
				strcpy(buf, "<a-f10>");
				buf[5] += *c - (K(KEY_F0) + 46);
			}
			else if(*c >= K(KEY_F0) + 10 && *c < K(KEY_F0) + 63)
			{
				strcpy(buf, "<f00>");
				buf[2] += (*c - K(KEY_F0))/10;
				buf[3] += (*c - K(KEY_F0))%10;
			}
			else if(iswcntrl(*c))
			{
				strcpy(buf, "<c-A>");
				buf[3] = tolower(buf[3] + *c - 1);
			}
			else
			{
				const wchar_t wchar[] = { *c, L'\0' };
				char *const mb = to_multibyte(wchar);
				copy_str(buf, sizeof(buf), mb);
				free(mb);
			}
			break;
	}
	return buf;
}

/* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */
/* vim: set cinoptions+=t0 filetype=c : */