/*	$OpenBSD: keymap.c,v 1.60 2022/10/20 18:59:24 op Exp $	*/

/* This file is in the public domain. */

/*
 * Keyboard maps.  This is character set dependent.  The terminal specific
 * parts of building the keymap has been moved to a better place.
 */

#include <sys/queue.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "def.h"
#include "kbd.h"

/*
 * initial keymap declarations, deepest first
 */

static PF cHcG[] = {
	ctrlg,			/* ^G */
	help_help		/* ^H */
};

static PF cHa[] = {
	apropos_command,	/* a */
	wallchart,		/* b */
	desckey			/* c */
};

struct KEYMAPE (2) helpmap = {
	2,
	2,
	rescan,
	{
		{
			CCHR('G'), CCHR('H'), cHcG, NULL
		},
		{
			'a', 'c', cHa, NULL
		}
	}
};

static PF cCsc[] = {
	cscallerfuncs,		/* c */
	csdefinition,		/* d */
	csegrep,		/* e */
	csfindfile,		/* f */
	rescan,			/* g */
	rescan,			/* h */
	csfindinc,		/* i */
	rescan,			/* j */
	rescan,			/* k */
	rescan,			/* l */
	rescan,			/* m */
	csnextmatch,		/* n */
	rescan,			/* o */
	csprevmatch,		/* p */
	rescan,			/* q */
	rescan, 		/* r */
	cssymbol,		/* s */
	csfindtext		/* t */
};

static struct KEYMAPE (1) cCsmap = {
	1,
	1,
	rescan,
	{
		{
			'c', 't', cCsc, NULL
		}
	}
};

static PF cCs[] = {
	NULL			/* s */
};

struct KEYMAPE (2) ccmap = {
	2,
	2,
	rescan,
	{
		{
			CCHR('@'), CCHR('@'), (PF[]){ rescan }, NULL
		},
		{
			's', 's', cCs, (KEYMAP *) & cCsmap
		}
	}
};

static PF cX4cF[] = {
	poptofile,		/* ^f */
	ctrlg			/* ^g */
};
static PF cX4b[] = {
	poptobuffer,		/* b */
	rescan,			/* c */
	rescan,			/* d */
	rescan,			/* e */
	poptofile		/* f */
};
static struct KEYMAPE (2) cX4map = {
	2,
	2,
	rescan,
	{
		{
			CCHR('F'), CCHR('G'), cX4cF, NULL
		},
		{
			'b', 'f', cX4b, NULL
		}
	}
};

static PF cXcB[] = {
	listbuffers,		/* ^B */
	quit,			/* ^C */
	rescan,			/* ^D */
	rescan,			/* ^E */
	filevisit,		/* ^F */
	ctrlg			/* ^G */
};

static PF cXcJ[] = {
	dired_jump,		/* ^J */
	rescan,			/* ^K */
	lowerregion,		/* ^L */
	rescan,			/* ^M */
	rescan,			/* ^N */
	deblank,		/* ^O */
	rescan,			/* ^P */
	togglereadonly,		/* ^Q */
	filevisitro,		/* ^R */
	filesave,		/* ^S */
	rescan,			/* ^T */
	upperregion,		/* ^U */
	filevisitalt,		/* ^V */
	filewrite,		/* ^W */
	swapmark		/* ^X */
};

static PF cXlp[] = {
	definemacro,		/* ( */
	finishmacro		/* ) */
};

static PF cX0[] = {
	delwind,		/* 0 */
	onlywind,		/* 1 */
	splitwind,		/* 2 */
	rescan,			/* 3 */
	NULL			/* 4 */
};

static PF cXeq[] = {
	showcpos		/* = */
};

static PF cXcar[] = {
	enlargewind,		/* ^ */
	rescan,			/* _ */
	next_error,		/* ` */
	rescan,			/* a */
	usebuffer,		/* b */
	rescan,			/* c */
	rescan,			/* d */
	executemacro,		/* e */
	setfillcol,		/* f */
	gotoline,		/* g */
	markbuffer,		/* h */
	fileinsert,		/* i */
	rescan,			/* j */
	killbuffer_cmd,		/* k */
	rescan,			/* l */
	rescan,			/* m */
	nextwind,		/* n */
	nextwind,		/* o */
	prevwind,		/* p */
	rescan,			/* q */
	rescan,			/* r */
	savebuffers,		/* s */
	rescan,			/* t */
	undo			/* u */
};

struct KEYMAPE (6) cXmap = {
	6,
	6,
	rescan,
	{
		{
			CCHR('B'), CCHR('G'), cXcB, NULL
		},
		{
			CCHR('J'), CCHR('X'), cXcJ, NULL
		},
		{
			'(', ')', cXlp, NULL
		},
		{
			'0', '4', cX0, (KEYMAP *) & cX4map
		},
		{
			'=', '=', cXeq, NULL
		},
		{
			'^', 'u', cXcar, NULL
		}
	}
};

static PF metacG[] = {
	ctrlg			/* ^G */
};

static PF metacV[] = {
	pagenext		/* ^V */
};

static PF metaspex[] = {
	justone,		/* space */
	shellcommand		/* ! */
};

static PF metapct[] = {
	queryrepl		/* % */
};

static PF metami[] = {
	poptag,                 /* * */
	rescan,                 /* + */
	rescan,                 /* , */
	negative_argument,	/* - */
	findtag,		/* . */
	rescan,			/* / */
	digit_argument,		/* 0 */
	digit_argument,		/* 1 */
	digit_argument,		/* 2 */
	digit_argument,		/* 3 */
	digit_argument,		/* 4 */
	digit_argument,		/* 5 */
	digit_argument,		/* 6 */
	digit_argument,		/* 7 */
	digit_argument,		/* 8 */
	digit_argument,		/* 9 */
	rescan,			/* : */
	rescan,			/* ; */
	gotobob,		/* < */
	rescan,			/* = */
	gotoeob			/* > */
};

static PF metasqf[] = {
	NULL,			/* [ */
	delwhite,		/* \ */
	rescan,			/* ] */
	joinline,		/* ^ */
	rescan,			/* _ */
	rescan,			/* ` */
	rescan,			/* a */
	backword,		/* b */
	capword,		/* c */
	delfword,		/* d */
	rescan,			/* e */
	forwword,		/* f */
	rescan,			/* g */
	markpara		/* h */
};

static PF metal[] = {
	lowerword,		/* l */
	backtoindent,		/* m */
	rescan,			/* n */
	rescan,			/* o */
	rescan,			/* p */
	fillpara,		/* q */
	backsearch,		/* r */
	forwsearch,		/* s */
	transposeword,		/* t */
	upperword,		/* u */
	backpage,		/* v */
	copyregion,		/* w */
	extend,			/* x */
	rescan,			/* y */
	zaptochar,		/* z */
	gotobop,		/* { */
	piperegion,		/* | */
	gotoeop			/* } */
};

static PF metasqlZ[] = {
	rescan			/* Z */
};

static PF metatilde[] = {
	notmodified,		/* ~ */
	delbword		/* DEL */
};

struct KEYMAPE (1) metasqlmap = {
	1,
	1,
	rescan,
	{
		{
			'Z', 'Z', metasqlZ, NULL
		}
	}
};

struct KEYMAPE (8) metamap = {
	8,
	8,
	rescan,
	{
		{
			CCHR('G'), CCHR('G'), metacG, NULL
		},
		{
			CCHR('V'), CCHR('V'), metacV, NULL
		},
		{
			' ', '!', metaspex, NULL
		},
		{
			'%', '%', metapct, NULL
		},
		{
			'*', '>', metami, NULL
		},
		{
			'[', 'h', metasqf, (KEYMAP *) &metasqlmap
		},
		{
			'l', '}', metal, NULL
		},
		{
			'~', CCHR('?'), metatilde, NULL
		}
	}
};

static PF fund_at[] = {
	setmark,		/* ^@ */
	gotobol,		/* ^A */
	backchar,		/* ^B */
	NULL,			/* ^C */
	forwdel,		/* ^D */
	gotoeol,		/* ^E */
	forwchar,		/* ^F */
	ctrlg,			/* ^G */
};

static PF fund_h[] = {
	NULL,			/* ^H */
};


/* ^I is selfinsert */
static PF fund_CJ[] = {
	lfindent,		/* ^J */
	killline,		/* ^K */
	reposition,		/* ^L */
	enewline,		/* ^M */
	forwline,		/* ^N */
	openline,		/* ^O */
	backline,		/* ^P */
	quote,			/* ^Q */
	backisearch,		/* ^R */
	forwisearch,		/* ^S */
	twiddle,		/* ^T */
	universal_argument,	/* ^U */
	forwpage,		/* ^V */
	killregion,		/* ^W */
	NULL,			/* ^X */
	yank,			/* ^Y */
	spawncli		/* ^Z */
};

static PF fund_esc[] = {
	NULL,			/* esc */
	rescan,			/* ^\ selfinsert is default on fundamental */
	rescan,			/* ^] */
	rescan,			/* ^^ */
	undo			/* ^_ */
};

static PF fund_del[] = {
	backdel			/* DEL */
};

static PF fund_cb[] = {
	showmatch		/* ) ] }  */
};

static struct KEYMAPE (8) fundmap = {
	8,
	8,
	selfinsert,
	{
		{
			CCHR('@'), CCHR('G'), fund_at, (KEYMAP *) & ccmap
		},
		{
			CCHR('H'), CCHR('H'), fund_h, (KEYMAP *) & helpmap
		},
		{
			CCHR('J'), CCHR('Z'), fund_CJ, (KEYMAP *) & cXmap
		},
		{
			CCHR('['), CCHR('_'), fund_esc, (KEYMAP *) & metamap
		},
		{
			')', ')', fund_cb, NULL
		},
		{
			']', ']', fund_cb, NULL
		},
		{
			'}', '}', fund_cb, NULL
		},
		{
			CCHR('?'), CCHR('?'), fund_del, NULL
		},
	}
};

static PF fill_sp[] = {
	fillword		/* ' ' */
};

static struct KEYMAPE (1) fillmap = {
	1,
	1,
	rescan,
	{
		{ ' ', ' ', fill_sp, NULL }
	}
};

static PF indent_lf[] = {
	enewline,		/* ^J */
	rescan,			/* ^K */
	rescan,			/* ^L */
	lfindent		/* ^M */
};

static struct KEYMAPE (1) indntmap = {
	1,
	1,
	rescan,
	{
		{
			CCHR('J'), CCHR('M'), indent_lf, NULL
		}
	}
};

#ifdef NOTAB
static PF notab_tab[] = {
	space_to_tabstop	/* ^I */
};

static struct KEYMAPE (1) notabmap = {
	1,
	1,
	rescan,
	{
		{
			CCHR('I'), CCHR('I'), notab_tab, NULL
		}
	}
};
#endif /* NOTAB */

static struct KEYMAPE (1) overwmap = {
	0,
	1,		/* 1 to avoid 0 sized array */
	rescan,
	{
		/* unused dummy entry for VMS C */
		{
			(KCHAR)0, (KCHAR)0, NULL, NULL
		}
	}
};


/*
 * The basic (root) keyboard map
 */
struct maps_s	fundamental_mode = { (KEYMAP *)&fundmap, "fundamental" };

/*
 * give names to the maps, for use by help etc. If the map is to be bindable,
 * it must also be listed in the function name table below with the same
 * name. Maps created dynamically currently don't get added here, thus are
 * unnamed. Modes are just named keymaps with functions to add/subtract them
 * from a buffer's list of modes.  If you change a mode name, change it in
 * modes.c also.
 */

static struct maps_s map_table[] = {
	{(KEYMAP *) &fillmap, "fill",},
	{(KEYMAP *) &indntmap, "indent",},
#ifdef NOTAB
	{(KEYMAP *) &notabmap, "notab",},
#endif /* NOTAB */
	{(KEYMAP *) &overwmap, "overwrite",},
	{(KEYMAP *) &metamap, "esc prefix",},
	{(KEYMAP *) &cXmap, "c-x prefix",},
	{(KEYMAP *) &cX4map, "c-x 4 prefix",},
	{(KEYMAP *) &helpmap, "help",},
	{NULL, NULL}
};

struct maps_s *maps;

void
maps_init(void)
{
	int	 i;
	struct maps_s	*mp;

	maps = &fundamental_mode;
	for (i = 0; map_table[i].p_name != NULL; i++) {
		mp = &map_table[i];
		mp->p_next = maps;
		maps = mp;
	}
}

/*
 * Insert a new (named) keymap at the head of the keymap list.
 */
int
maps_add(KEYMAP *map, const char *name)
{
	struct maps_s	*mp;

	if ((mp = malloc(sizeof(*mp))) == NULL)
		return (FALSE);

	mp->p_name = name;
	mp->p_map = map;
	mp->p_next = maps;
	maps = mp;

	return (TRUE);
}

struct maps_s *
name_mode(const char *name)
{
	struct maps_s	*mp;

	for (mp = maps; mp != NULL; mp = mp->p_next)
		if (strcmp(mp->p_name, name) == 0)
			return (mp);
	return (NULL);
}

KEYMAP *
name_map(const char *name)
{
	struct maps_s	*mp;

	return ((mp = name_mode(name)) == NULL ? NULL : mp->p_map);
}
