<commit_before>/*
 * emrl.c -- emrl line editing library functions
 *
 * Copyright (C) 2017 <NAME> (<EMAIL>)
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */

#include <assert.h>
#include <ctype.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>

#include "emrl.h"

#define PRINT(str) (void)p_this->fputs(str, p_this->file)

#define SEQ_STEP_RIGHT "\033[C"
#define SEQ_STEP_LEFT "\b"
#define SEQ_DELETE_FORWARD "\033[P"
#define SEQ_DELETE_BACK "\b\033[P"
#define SEQ_DELETE_FROM_END "\b \b"
#define SEQ_INSERT_SPACE "\033[@"
#define SEQ_ERASE_TO_END "\033[K"

// TODO
// baud rate simulation in examples
// more history api -> help choose what to add
// completion
// option to build without snprintf
// static initialisation macro
// recognise more keys - insert, pgup, pgdown, home, end
// C++
// utf8 support?
// use BEL?
// attempt to handle print errors?


enum rp_type
{
	rp_insert,
	rp_erase
};

static inline void process_escape_state(struct emrl_res *p_this, char chr);
static inline void interpret_csi_escape(struct emrl_res *p_this);
static inline void erase_forward(struct emrl_res *p_this);
static inline void erase_back(struct emrl_res *p_this);
static inline void add_string(struct emrl_res *p_this, char *p_str);
#if !defined(USE_INSERT_ESCAPE_SEQUENCE) || !defined(USE_DELETE_ESCAPE_SEQUENCE)
static inline void reprint_from_cursor(struct emrl_res *p_this, enum rp_type type, size_t back_mv);
#endif
static inline void reset_esc(struct emrl_res *p_this, bool known);
static inline char *hist_search_forward(struct emrl_res *p_this, char *p_entry);
static inline char *hist_search_backward(struct emrl_res *p_this, char *p_entry);
static inline void hist_show_prev(struct emrl_res *p_this);
static inline void hist_show_next(struct emrl_res *p_this);
static inline void hist_show_current(struct emrl_res *p_this);
static inline void clear_from_prompt(struct emrl_res *p_this);
static inline void copy_on_edit(struct emrl_res *p_this);
static inline unsigned char_to_printable(unsigned char chr, char *p_print_str);

void emrl_init(struct emrl_res *p_this, emrl_fputs_func fputs, emrl_file file, const char *delim)
{
	p_this->fputs = fputs;
	p_this->file = file;
	p_this->delim = p_this->p_delim = delim;

	p_this->p_esc = p_this->esc_buf;
	p_this->p_esc_last = p_this->esc_buf + sizeof p_this->esc_buf - 1;

	p_this->p_cursor = p_this->p_cmd_free = p_this->cmd_buf;
	p_this->p_cmd_last = p_this->cmd_buf + sizeof p_this->cmd_buf - 1;
	p_this->esc_state = emrl_esc_none;

	struct emrl_history *ph = &p_this->history;
	ph->p_oldest = ph->p_newest = ph->p_current = NULL;
	ph->p_put = ph->buf + 1;
	ph->p_buf_last = ph->buf + sizeof ph->buf - 1;

	// Initialise first and last byte of history buffer to zero to delimit the buffer boundaries.
	// This enables faster searching using strchr (as opposed to a loop with bounds checks),
	// provided of course that no data is written to these bytes.
	ph->buf[0] = '\0';
	ph->buf[sizeof ph->buf - 1] = '\0';
	// Need a null delimiting start of unwritten oldest entry
	ph->buf[sizeof ph->buf - 2] = '\0';
}


char *emrl_process_char(struct emrl_res *p_this, char chr)
{
	char str_buf[5];

	if(emrl_esc_none != p_this->esc_state)
	{
		process_escape_state(p_this, chr);
		return NULL;
	}

	if(chr == *p_this->p_delim)
	{
		++p_this->p_delim;
		if('\0' == *p_this->p_delim)
		{
			*p_this->p_cmd_free = '\0';
			p_this->p_delim = p_this->delim;
			p_this->p_cursor = p_this->p_cmd_free = p_this->cmd_buf;

			return p_this->cmd_buf;
		}
	}
	else
	{
		p_this->p_delim = p_this->delim;
	}

	switch(chr)
	{
		case '\r':
		case '\n':
			// Ignore (unless in delim string)
			break;

		case EMRL_ASCII_ESC:
			p_this->esc_state = emrl_esc_new;
			break;

		case EMRL_ASCII_DEL:
			erase_back(p_this);
			break;

		default:
			char_to_printable(chr, str_buf);
			add_string(p_this, str_buf);
			break;
	}

	return NULL;
}


void emrl_add_to_history(struct emrl_res *p_this, char *p_command)
{
	struct emrl_history *ph = &p_this->history;
	size_t cmd_len = strlen(p_command) + 1;<commit_msg>Erase back when backspace character is received<commit_after>/*
 * emrl.c -- emrl line editing library functions
 *
 * Copyright (C) 2017 <NAME> (<EMAIL>)
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */

#include <assert.h>
#include <ctype.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>

#include "emrl.h"

#define PRINT(str) (void)p_this->fputs(str, p_this->file)

#define SEQ_STEP_RIGHT "\033[C"
#define SEQ_STEP_LEFT "\b"
#define SEQ_DELETE_FORWARD "\033[P"
#define SEQ_DELETE_BACK "\b\033[P"
#define SEQ_DELETE_FROM_END "\b \b"
#define SEQ_INSERT_SPACE "\033[@"
#define SEQ_ERASE_TO_END "\033[K"

// TODO
// baud rate simulation in examples
// check historic support for escape sequences above
// more history api -> help choose what to add
// completion
// option to build without snprintf
// static initialisation macro
// recognise more keys - insert, pgup, pgdown, home, end
// C++
// utf8 support?
// use BEL?
// attempt to handle print errors?


enum rp_type
{
	rp_insert,
	rp_erase
};

static inline void process_escape_state(struct emrl_res *p_this, char chr);
static inline void interpret_csi_escape(struct emrl_res *p_this);
static inline void erase_forward(struct emrl_res *p_this);
static inline void erase_back(struct emrl_res *p_this);
static inline void add_string(struct emrl_res *p_this, char *p_str);
#if !defined(USE_INSERT_ESCAPE_SEQUENCE) || !defined(USE_DELETE_ESCAPE_SEQUENCE)
static inline void reprint_from_cursor(struct emrl_res *p_this, enum rp_type type, size_t back_mv);
#endif
static inline void reset_esc(struct emrl_res *p_this, bool known);
static inline char *hist_search_forward(struct emrl_res *p_this, char *p_entry);
static inline char *hist_search_backward(struct emrl_res *p_this, char *p_entry);
static inline void hist_show_prev(struct emrl_res *p_this);
static inline void hist_show_next(struct emrl_res *p_this);
static inline void hist_show_current(struct emrl_res *p_this);
static inline void clear_from_prompt(struct emrl_res *p_this);
static inline void copy_on_edit(struct emrl_res *p_this);
static inline unsigned char_to_printable(unsigned char chr, char *p_print_str);

void emrl_init(struct emrl_res *p_this, emrl_fputs_func fputs, emrl_file file, const char *delim)
{
	p_this->fputs = fputs;
	p_this->file = file;
	p_this->delim = p_this->p_delim = delim;

	p_this->p_esc = p_this->esc_buf;
	p_this->p_esc_last = p_this->esc_buf + sizeof p_this->esc_buf - 1;

	p_this->p_cursor = p_this->p_cmd_free = p_this->cmd_buf;
	p_this->p_cmd_last = p_this->cmd_buf + sizeof p_this->cmd_buf - 1;
	p_this->esc_state = emrl_esc_none;

	struct emrl_history *ph = &p_this->history;
	ph->p_oldest = ph->p_newest = ph->p_current = NULL;
	ph->p_put = ph->buf + 1;
	ph->p_buf_last = ph->buf + sizeof ph->buf - 1;

	// Initialise first and last byte of history buffer to zero to delimit the buffer boundaries.
	// This enables faster searching using strchr (as opposed to a loop with bounds checks),
	// provided of course that no data is written to these bytes.
	ph->buf[0] = '\0';
	ph->buf[sizeof ph->buf - 1] = '\0';
	// Need a null delimiting start of unwritten oldest entry
	ph->buf[sizeof ph->buf - 2] = '\0';
}


char *emrl_process_char(struct emrl_res *p_this, char chr)
{
	char str_buf[5];

	if(emrl_esc_none != p_this->esc_state)
	{
		process_escape_state(p_this, chr);
		return NULL;
	}

	if(chr == *p_this->p_delim)
	{
		++p_this->p_delim;
		if('\0' == *p_this->p_delim)
		{
			*p_this->p_cmd_free = '\0';
			p_this->p_delim = p_this->delim;
			p_this->p_cursor = p_this->p_cmd_free = p_this->cmd_buf;

			return p_this->cmd_buf;
		}
	}
	else
	{
		p_this->p_delim = p_this->delim;
	}

	switch(chr)
	{
		case '\b':
			erase_back(p_this);
			break;

		case '\r':
		case '\n':
			// Ignore (unless in delim string)
			break;

		case EMRL_ASCII_ESC:
			p_this->esc_state = emrl_esc_new;
			break;

		case EMRL_ASCII_DEL:
			erase_back(p_this);
			break;

		default:
			char_to_printable(chr, str_buf);
			add_string(p_this, str_buf);
			break;
	}

	return NULL;
}


void emrl_add_to_history(struct emrl_res *p_this, char *p_command)
{
	struct emrl_history *ph = &p_this->history;
	size_t cmd_len = strlen(p_command) + 1;
{'fraction_non_alphanumeric': 0.12026673017385092, 'fraction_numerical': 0.008097165991902834, 'mean_word_length': 4.543894389438944, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 113, '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': '5223688', 'n_tokens_mistral': 3382, 'n_tokens_neox': 3189, 'n_words': 834}