<commit_before>#include "tinyrl.h"

#include <assert.h>
#include <ctype.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <sys/ioctl.h>

#define KEYMAP_SIZE 256

struct tinyrl_keymap {
	tinyrl_key_func_t *handler[KEYMAP_SIZE];
	struct tinyrl_keymap *keymap[KEYMAP_SIZE];
	void *context[KEYMAP_SIZE];
};

/* define the class member data and virtual methods */
struct tinyrl {
	FILE *istream;
	FILE *ostream;
	const char *line;
	unsigned max_line_length;
	const char *prompt;
	char *buffer;
	size_t buffer_size;
	bool done;
	unsigned point;
	unsigned end;
	char *kill_string;
	struct tinyrl_keymap *keymap;

	char echo_char;
	bool echo_enabled;
	bool isatty;

	char *last_buffer;
	size_t last_end;
	size_t last_rows;
	size_t last_point_rows;
};

#define ESCAPESTR "\x1b"
#define ESCAPE 27
#define BACKSPACE 127

/*-------------------------------------------------------- */
static void tinyrl_vt100_clear_screen(const struct tinyrl *this)
{
	tinyrl_printf(this, "\x1b[2J");
}

/*-------------------------------------------------------- */
static void tinyrl_vt100_erase_line_end(const struct tinyrl *this)
{
	tinyrl_printf(this, "\x1b[0K");
}

/*-------------------------------------------------------- */
static void tinyrl_vt100_erase_line(const struct tinyrl *this)
{
	tinyrl_printf(this, "\x1b[2K");
}

/*-------------------------------------------------------- */
static void tinyrl_vt100_cursor_up(const struct tinyrl *this, unsigned count)
{
	tinyrl_printf(this, "\x1b[%dA", count);
}

/*-------------------------------------------------------- */
static void tinyrl_vt100_cursor_down(const struct tinyrl *this, unsigned count)
{
	tinyrl_printf(this, "\x1b[%dB", count);
}

/*-------------------------------------------------------- */
static void tinyrl_vt100_cursor_forward(const struct tinyrl *this, unsigned count)
{
	tinyrl_printf(this, "\x1b[%dC", count);
}

/*-------------------------------------------------------- */
static void tinyrl_vt100_cursor_home(const struct tinyrl *this)
{
	tinyrl_printf(this, "\x1b[H");
}

/*----------------------------------------------------------------------- */
static void tty_set_raw_mode(FILE *istream, struct termios *old_termios)
{
	struct termios new_termios;
	int fd = fileno(istream);
	int status;

	status = tcgetattr(fd, old_termios);
	if (-1 != status) {
		status = tcgetattr(fd, &new_termios);
		assert(-1 != status);
		new_termios.c_iflag = 0;
		new_termios.c_oflag = OPOST | ONLCR;
		new_termios.c_lflag = 0;
		new_termios.c_cc[VMIN] = 1;
		new_termios.c_cc[VTIME] = 0;
		/* Do the mode switch */
		status = tcsetattr(fd, TCSAFLUSH, &new_termios);
		assert(-1 != status);
	}
}

/*----------------------------------------------------------------------- */
static void tty_restore_mode(FILE *istream, struct termios *old_termios)
{
	int fd = fileno(istream);

	tcsetattr(fd, TCSAFLUSH, old_termios);
}

/*----------------------------------------------------------------------- */
/*
   This is called whenever a line is edited in any way.
   It signals that if we are currently viewing a history line we should transfer it
   to the current buffer
   */
static void changed_line(struct tinyrl *this)
{
	/* if the current line is not our buffer then make it so */
	if (this->line != this->buffer) {
		/* replace the current buffer with the new details */
		free(this->buffer);
		this->line = this->buffer = strdup(this->line);
		this->buffer_size = strlen(this->buffer);
		assert(this->line);
	}
}

/*----------------------------------------------------------------------- */
static bool tinyrl_key_default(void *context, int key)
{
	struct tinyrl *this = context;
	bool result;
	if (key > 31) {
		char tmp = key & 0xFF;
		result = tinyrl_insert_text_len(this, &tmp, 1);
	} else {
		char tmp[10];
		sprintf(tmp, "~%d", key);
		/* inject control characters as ~N where N is the ASCII code */
		result = tinyrl_insert_text(this, tmp);
	}
	return result;
}

/*-------------------------------------------------------- */
static bool tinyrl_key_interrupt(void *context, int key)
{
	struct tinyrl *this = context;

	tinyrl_delete_text(this, 0, this->end);
	this->done = true;

	return true;
}

/*-------------------------------------------------------- */
static bool tinyrl_key_start_of_line(void *context, int key)
{
	struct tinyrl *this = context;

	/* set the insertion point to the start of the line */
	this->point = 0;
	return true;
}

/*-------------------------------------------------------- */
static bool tinyrl_key_end_of_line(void *context, int key)
{
	struct tinyrl *this = context;

	/* set the insertion point to the end of the line */
	this->point = this->end;
	return true;
}

/*-------------------------------------------------------- */
static bool tinyrl_key_kill(void *context, int key)
{
	struct tinyrl *this = context;

	/* release any old kill string */
	free(this->kill_string);

	/* store the killed string */
	this->kill_string = strdup(&this->buffer[this->point]);

	/* delete the text to the end of the line */
	tinyrl_delete_text(this, this->point, this->end);
	return true;
}

/*-------------------------------------------------------- */
static bool tinyrl_key_yank(void *context, int key)
{
	struct tinyrl *this = context;
	bool result = false;
	if (this->kill_string) {
		/* insert the kill string at the current insertion point */
		result = tinyrl_insert_text(this, this->kill_string);
	}
	return result;
}

/*-------------------------------------------------------- */
static bool tinyrl_key_crlf(void *context, int key)
{
	struct tinyrl *this = context;

	tinyrl_crlf(this);
	this->done = true;
	return true;
}

/*-------------------------------------------------------- */
static bool tinyrl_key_left(void *context, int key)
{
	struct tinyrl *this = context;
	bool result = false;
	if (this->point > 0) {
		this->point--;
		result = true;
	}
	return result;
}

/*-------------------------------------------------------- */
static bool tinyrl_key_right(void *context, int key)
{
	struct tinyrl *this = context;
	bool result = false;
	if (this->point < this->end) {
		this->point++;
		result = true;
	}
	return result;
}

/*-------------------------------------------------------- */
static bool tinyrl_key_backspace(void *context, int key)
{
	struct tinyrl *this = context;
	bool result = false;
	if (this->point) {
		this->point--;
		tinyrl_delete_text(this, this->point, this->point + 1);
		result = true;
	}
	return result;
}

/*-------------------------------------------------------- */
static bool tinyrl_key_delete(void *context, int key)
{
	struct tinyrl *this = context;
	bool result = false;
	if (this->point < this->end) {
		tinyrl_delete_text(this, this->point, this->point + 1);
		result = true;
	}
	return result;
}

/*-------------------------------------------------------- */
static bool tinyrl_key_clear_screen(void *context, int key)
{
	struct tinyrl *this = context;

	tinyrl_vt100_clear_screen(this);
	tinyrl_vt100_cursor_home(this);
	tinyrl_reset_line_state(this);
	return true;
}

/*-------------------------------------------------------- */
static bool tinyrl_key_erase_line(void *context, int key)
{
	struct tinyrl *this = context;

	tinyrl_delete_text(this, 0, this->point);
	this->point = 0;
	return true;
}

/*-------------------------------------------------------- */
static struct tinyrl_keymap *tinyrl_keymap_new()
{
	struct tinyrl_keymap *keymap;
	int i;

	keymap = malloc(sizeof(*keymap));

	for (i = 0; i < KEYMAP_SIZE; i++) {
		keymap->handler[i] = NULL;
		keymap->keymap[i] = NULL;
		keymap->context[i] = NULL;
	}

	return keymap;
}

static void tinyrl_keymap_free(struct tinyrl_keymap *keymap)
{
	int i;

	for (i = 0; i < KEYMAP_SIZE; i++)
		if (keymap->keymap[i])
			tinyrl_keymap_free(keymap->keymap[i]);
	free(keymap);
}

/*-------------------------------------------------------- */
static void tinyrl_fini(struct tinyrl *this)
{
	/* free up any dynamic strings */
	free(this->buffer);
	this->buffer = NULL;
	free(this->kill_string);
	this->kill_string = NULL;
	free(this->last_buffer);
	tinyrl_keymap_free(this->keymap);
}

/*-------------------------------------------------------- */
static void
tinyrl_init(struct tinyrl *this, FILE * instream, FILE * outstream)
{
	int i;

	this->keymap = tinyrl_keymap_new();
	for (i = 32; i < 256; i++)
		tinyrl_bind_key(this, i, tinyrl_key_default, this);
	tinyrl_bind_key(this, '\r', tinyrl_key_crlf, this);
	tinyrl_bind_key(this, '\n', tinyrl_key_crlf, this);
	tinyrl_bind_key(this, CTRL('C'), tinyrl_key_interrupt, this);
	tinyrl_bind_key(this, BACKSPACE, tinyrl_key_backspace, this);
	tinyrl_bind_key(this, CTRL('H'), tinyrl_key_backspace, this);
	tinyrl_bind_key(this, CTRL('D'), tinyrl_key_delete, this);
	tinyrl_bind_key(this, CTRL('L'), tinyrl_key_clear_screen, this);
	tinyrl_bind_key(this, CTRL('U'), tinyrl_key_erase_line, this);
	tinyrl_bind_key(this, CTRL('A'), tinyrl_key_start_of_line, this);
	tinyrl_bind_key(this, CTRL('E'), tinyrl_key_end_of_line, this);
	tinyrl_bind_key(this, CTRL('K'), tinyrl_key_kill, this);
	tinyrl_bind_key(this, CTRL('Y'), tinyrl_key_yank, this);
	tinyrl_bind_special(this, TINYRL_KEY_RIGHT, tinyrl_key_right, this);
	tinyrl_bind_special(this, TINYRL_KEY_LEFT, tinyrl_key_left, this);
	tinyrl_bind_special(this, TINYRL_KEY_HOME, tinyrl_key_start_of_line, this);
	tinyrl_bind_special(this, TINYRL_KEY_END, tinyrl_key_end_of_line, this);
	tinyrl_bind_special(this, TINYRL_KEY_DELETE, tinyrl_key_delete, this);

	this->line = NULL;
	this->max_line_length = 0;
	this->prompt = NULL;
	this->buffer = NULL;
	this->buffer_size = 0;
	this->done = false;
	this->point = 0;<commit_msg>Add binding for insert<commit_after>#include "tinyrl.h"

#include <assert.h>
#include <ctype.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <sys/ioctl.h>

#define KEYMAP_SIZE 256

struct tinyrl_keymap {
	tinyrl_key_func_t *handler[KEYMAP_SIZE];
	struct tinyrl_keymap *keymap[KEYMAP_SIZE];
	void *context[KEYMAP_SIZE];
};

/* define the class member data and virtual methods */
struct tinyrl {
	FILE *istream;
	FILE *ostream;
	const char *line;
	unsigned max_line_length;
	const char *prompt;
	char *buffer;
	size_t buffer_size;
	bool done;
	unsigned point;
	unsigned end;
	char *kill_string;
	struct tinyrl_keymap *keymap;

	char echo_char;
	bool echo_enabled;
	bool isatty;

	char *last_buffer;
	size_t last_end;
	size_t last_rows;
	size_t last_point_rows;
};

#define ESCAPESTR "\x1b"
#define ESCAPE 27
#define BACKSPACE 127

/*-------------------------------------------------------- */
static void tinyrl_vt100_clear_screen(const struct tinyrl *this)
{
	tinyrl_printf(this, "\x1b[2J");
}

/*-------------------------------------------------------- */
static void tinyrl_vt100_erase_line_end(const struct tinyrl *this)
{
	tinyrl_printf(this, "\x1b[0K");
}

/*-------------------------------------------------------- */
static void tinyrl_vt100_erase_line(const struct tinyrl *this)
{
	tinyrl_printf(this, "\x1b[2K");
}

/*-------------------------------------------------------- */
static void tinyrl_vt100_cursor_up(const struct tinyrl *this, unsigned count)
{
	tinyrl_printf(this, "\x1b[%dA", count);
}

/*-------------------------------------------------------- */
static void tinyrl_vt100_cursor_down(const struct tinyrl *this, unsigned count)
{
	tinyrl_printf(this, "\x1b[%dB", count);
}

/*-------------------------------------------------------- */
static void tinyrl_vt100_cursor_forward(const struct tinyrl *this, unsigned count)
{
	tinyrl_printf(this, "\x1b[%dC", count);
}

/*-------------------------------------------------------- */
static void tinyrl_vt100_cursor_home(const struct tinyrl *this)
{
	tinyrl_printf(this, "\x1b[H");
}

/*----------------------------------------------------------------------- */
static void tty_set_raw_mode(FILE *istream, struct termios *old_termios)
{
	struct termios new_termios;
	int fd = fileno(istream);
	int status;

	status = tcgetattr(fd, old_termios);
	if (-1 != status) {
		status = tcgetattr(fd, &new_termios);
		assert(-1 != status);
		new_termios.c_iflag = 0;
		new_termios.c_oflag = OPOST | ONLCR;
		new_termios.c_lflag = 0;
		new_termios.c_cc[VMIN] = 1;
		new_termios.c_cc[VTIME] = 0;
		/* Do the mode switch */
		status = tcsetattr(fd, TCSAFLUSH, &new_termios);
		assert(-1 != status);
	}
}

/*----------------------------------------------------------------------- */
static void tty_restore_mode(FILE *istream, struct termios *old_termios)
{
	int fd = fileno(istream);

	tcsetattr(fd, TCSAFLUSH, old_termios);
}

/*----------------------------------------------------------------------- */
/*
   This is called whenever a line is edited in any way.
   It signals that if we are currently viewing a history line we should transfer it
   to the current buffer
   */
static void changed_line(struct tinyrl *this)
{
	/* if the current line is not our buffer then make it so */
	if (this->line != this->buffer) {
		/* replace the current buffer with the new details */
		free(this->buffer);
		this->line = this->buffer = strdup(this->line);
		this->buffer_size = strlen(this->buffer);
		assert(this->line);
	}
}

/*----------------------------------------------------------------------- */
static bool tinyrl_key_default(void *context, int key)
{
	struct tinyrl *this = context;
	bool result;
	if (key > 31) {
		char tmp = key & 0xFF;
		result = tinyrl_insert_text_len(this, &tmp, 1);
	} else {
		char tmp[10];
		sprintf(tmp, "~%d", key);
		/* inject control characters as ~N where N is the ASCII code */
		result = tinyrl_insert_text(this, tmp);
	}
	return result;
}

/*-------------------------------------------------------- */
static bool tinyrl_key_interrupt(void *context, int key)
{
	struct tinyrl *this = context;

	tinyrl_delete_text(this, 0, this->end);
	this->done = true;

	return true;
}

/*-------------------------------------------------------- */
static bool tinyrl_key_start_of_line(void *context, int key)
{
	struct tinyrl *this = context;

	/* set the insertion point to the start of the line */
	this->point = 0;
	return true;
}

/*-------------------------------------------------------- */
static bool tinyrl_key_end_of_line(void *context, int key)
{
	struct tinyrl *this = context;

	/* set the insertion point to the end of the line */
	this->point = this->end;
	return true;
}

/*-------------------------------------------------------- */
static bool tinyrl_key_kill(void *context, int key)
{
	struct tinyrl *this = context;

	/* release any old kill string */
	free(this->kill_string);

	/* store the killed string */
	this->kill_string = strdup(&this->buffer[this->point]);

	/* delete the text to the end of the line */
	tinyrl_delete_text(this, this->point, this->end);
	return true;
}

/*-------------------------------------------------------- */
static bool tinyrl_key_yank(void *context, int key)
{
	struct tinyrl *this = context;
	bool result = false;
	if (this->kill_string) {
		/* insert the kill string at the current insertion point */
		result = tinyrl_insert_text(this, this->kill_string);
	}
	return result;
}

/*-------------------------------------------------------- */
static bool tinyrl_key_crlf(void *context, int key)
{
	struct tinyrl *this = context;

	tinyrl_crlf(this);
	this->done = true;
	return true;
}

/*-------------------------------------------------------- */
static bool tinyrl_key_left(void *context, int key)
{
	struct tinyrl *this = context;
	bool result = false;
	if (this->point > 0) {
		this->point--;
		result = true;
	}
	return result;
}

/*-------------------------------------------------------- */
static bool tinyrl_key_right(void *context, int key)
{
	struct tinyrl *this = context;
	bool result = false;
	if (this->point < this->end) {
		this->point++;
		result = true;
	}
	return result;
}

/*-------------------------------------------------------- */
static bool tinyrl_key_backspace(void *context, int key)
{
	struct tinyrl *this = context;
	bool result = false;
	if (this->point) {
		this->point--;
		tinyrl_delete_text(this, this->point, this->point + 1);
		result = true;
	}
	return result;
}

/*-------------------------------------------------------- */
static bool tinyrl_key_delete(void *context, int key)
{
	struct tinyrl *this = context;
	bool result = false;
	if (this->point < this->end) {
		tinyrl_delete_text(this, this->point, this->point + 1);
		result = true;
	}
	return result;
}

/*-------------------------------------------------------- */
static bool tinyrl_key_clear_screen(void *context, int key)
{
	struct tinyrl *this = context;

	tinyrl_vt100_clear_screen(this);
	tinyrl_vt100_cursor_home(this);
	tinyrl_reset_line_state(this);
	return true;
}

/*-------------------------------------------------------- */
static bool tinyrl_key_erase_line(void *context, int key)
{
	struct tinyrl *this = context;

	tinyrl_delete_text(this, 0, this->point);
	this->point = 0;
	return true;
}

/*-------------------------------------------------------- */
static struct tinyrl_keymap *tinyrl_keymap_new()
{
	struct tinyrl_keymap *keymap;
	int i;

	keymap = malloc(sizeof(*keymap));

	for (i = 0; i < KEYMAP_SIZE; i++) {
		keymap->handler[i] = NULL;
		keymap->keymap[i] = NULL;
		keymap->context[i] = NULL;
	}

	return keymap;
}

static void tinyrl_keymap_free(struct tinyrl_keymap *keymap)
{
	int i;

	for (i = 0; i < KEYMAP_SIZE; i++)
		if (keymap->keymap[i])
			tinyrl_keymap_free(keymap->keymap[i]);
	free(keymap);
}

/*-------------------------------------------------------- */
static void tinyrl_fini(struct tinyrl *this)
{
	/* free up any dynamic strings */
	free(this->buffer);
	this->buffer = NULL;
	free(this->kill_string);
	this->kill_string = NULL;
	free(this->last_buffer);
	tinyrl_keymap_free(this->keymap);
}

/*-------------------------------------------------------- */
static void
tinyrl_init(struct tinyrl *this, FILE * instream, FILE * outstream)
{
	int i;

	this->keymap = tinyrl_keymap_new();
	for (i = 32; i < 256; i++)
		tinyrl_bind_key(this, i, tinyrl_key_default, this);
	tinyrl_bind_key(this, '\r', tinyrl_key_crlf, this);
	tinyrl_bind_key(this, '\n', tinyrl_key_crlf, this);
	tinyrl_bind_key(this, CTRL('C'), tinyrl_key_interrupt, this);
	tinyrl_bind_key(this, BACKSPACE, tinyrl_key_backspace, this);
	tinyrl_bind_key(this, CTRL('H'), tinyrl_key_backspace, this);
	tinyrl_bind_key(this, CTRL('D'), tinyrl_key_delete, this);
	tinyrl_bind_key(this, CTRL('L'), tinyrl_key_clear_screen, this);
	tinyrl_bind_key(this, CTRL('U'), tinyrl_key_erase_line, this);
	tinyrl_bind_key(this, CTRL('A'), tinyrl_key_start_of_line, this);
	tinyrl_bind_key(this, CTRL('E'), tinyrl_key_end_of_line, this);
	tinyrl_bind_key(this, CTRL('K'), tinyrl_key_kill, this);
	tinyrl_bind_key(this, CTRL('Y'), tinyrl_key_yank, this);
	tinyrl_bind_special(this, TINYRL_KEY_RIGHT, tinyrl_key_right, this);
	tinyrl_bind_special(this, TINYRL_KEY_LEFT, tinyrl_key_left, this);
	tinyrl_bind_special(this, TINYRL_KEY_HOME, tinyrl_key_start_of_line, this);
	tinyrl_bind_special(this, TINYRL_KEY_END, tinyrl_key_end_of_line, this);
	tinyrl_bind_special(this, TINYRL_KEY_INSERT, NULL, NULL);
	tinyrl_bind_special(this, TINYRL_KEY_DELETE, tinyrl_key_delete, this);

	this->line = NULL;
	this->max_line_length = 0;
	this->prompt = NULL;
	this->buffer = NULL;
	this->buffer_size = 0;
	this->done = false;
	this->point = 0;
{'fraction_non_alphanumeric': 0.2724102564102564, 'fraction_numerical': 0.007794871794871795, 'mean_word_length': 2.276928247353386, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 0, '>': 143, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2507338', 'n_tokens_mistral': 6722, 'n_tokens_neox': 6452, 'n_words': 1532}