aboutsummaryrefslogtreecommitdiffstats
path: root/libterminput_read_bracketed_paste__.c
blob: b580b701bf11f730b75c57fa63b327395b6c3b4c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
/* See LICENSE file for copyright and license details. */
#include "common.h"


int
libterminput_read_bracketed_paste__(int fd, union libterminput_input *input, struct libterminput_state *ctx)
{
	ssize_t r;
	size_t n;
	size_t i;

	/* Unfortunately there is no standard for how to handle pasted ESC's,
	 * not even ESC [201~ or ESC ESC. Terminates seem to just paste ESC as
	 * is, so we cannot do anything about them, however, a good terminal
	 * would stop the paste at the ~ in ESC [201~, send ~ as normal, and
	 * then continue the brackated paste mode. */

	/* Check for bracketed paste end marker to output LIBTERMINPUT_BRACKETED_PASTE_END
	 * and stop, and read more if we don't have it; the marker will be at the
	 * beginning as the function will stop when it encounteres it and output the
	 * text pasted before it */
	if (ctx->stored_head - ctx->stored_tail) {
		/* If we have input buffered, unpause and handle it */
		ctx->paused = 0;
		n = ctx->stored_head - ctx->stored_tail;
		if (!strncmp(&ctx->stored[ctx->stored_tail], "\033[201~", n < 6U ? n : 6U)) {
			/* If starting with bracketed paste end marker, output LIBTERMINPUT_BRACKETED_PASTE_END, */
			if (n >= 6U) {
				ctx->stored_tail += 6U;
				if (ctx->stored_tail == ctx->stored_head)
					ctx->stored_tail = ctx->stored_head = 0;
				ctx->bracketed_paste = 0;
				input->type = LIBTERMINPUT_BRACKETED_PASTE_END;
				return 1;
			}
			/* otherwise, but if the buffered input is a truncating of the marker,
			 * move over the data from the stored input buffer to the input buffer
			 * and store continue reading input */
			input->text.nbytes = ctx->stored_head - ctx->stored_tail;
			memcpy(input->text.bytes, &ctx->stored[ctx->stored_tail], input->text.nbytes);
			r = read(fd, &input->text.bytes[input->text.nbytes], sizeof(input->text.bytes) - input->text.nbytes);
			if (r <= 0)
				return (int)r;
			input->text.nbytes += (size_t)r;
			ctx->stored_head = ctx->stored_tail = 0;
		} else {
			/* If the buffered input does not begin with the bracketed paste end marker,
			 * or a truncation of it, move over the data from the stored input buffer
			 * to the input buffer */
			input->text.nbytes = ctx->stored_head - ctx->stored_tail;
			memcpy(input->text.bytes, &ctx->stored[ctx->stored_tail], input->text.nbytes);
			ctx->stored_head = ctx->stored_tail = 0;
		}
	} else {
		/* If we don't have any input buffered, read some */
		r = read(fd, input->text.bytes, sizeof(input->text.bytes));
		if (r <= 0)
			return (int)r;
		input->text.nbytes = (size_t)r;
	}

	/* Count the number of bytes available before a bracketed paste end
	 * marker, or a truncation of it at the end of the input buffer */
	for (n = 0; n + 5U < input->text.nbytes; n++) {
		if (!strncmp(&input->text.bytes[n], "\033[201~", 6U))
			break;
	}
	for (i = 5U; i--;) {
		if (n + i < input->text.nbytes) {
			if (!strncmp(&input->text.bytes[n], "\033[201~", i + 1U))
				break;
			n += 1;
		}
	}

	/* Of there was pasted input, output it */
	if (n) {
		ctx->stored_tail = 0;
		ctx->stored_head = input->text.nbytes - n;
		memcpy(ctx->stored, &input->text.bytes[n], ctx->stored_head);
		input->text.nbytes = n;
		input->text.type = LIBTERMINPUT_TEXT;
		return 1;
	}

	/* If the input is solely a truncation of the bracketed paste
	 * end marker, output that we do not have any complete input,
	 * and pause as the available buffered input is incomplete */
	if (input->text.nbytes < 6U) {
		memcpy(ctx->stored, input->text.bytes, input->text.nbytes);
		ctx->stored_tail = 0;
		ctx->stored_head = input->text.nbytes;
		ctx->paused = 1;
		NOTHING(input);
		return 1;
	}

	/* If the input starts with a bracketed paste end marker,
	 * output it and store the rest of the input buffer for
	 * later processing */
	ctx->stored_tail = 0;
	ctx->stored_head = input->text.nbytes - 6U;
	memcpy(ctx->stored, &input->text.bytes[6], ctx->stored_head);
	if (ctx->stored_tail == ctx->stored_head)
		ctx->stored_tail = ctx->stored_head = 0;
	ctx->bracketed_paste = 0;
	input->type = LIBTERMINPUT_BRACKETED_PASTE_END;
	return 1;
}