18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * This module exports the functions:
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *     'int set_selection_user(struct tiocl_selection __user *,
68c2ecf20Sopenharmony_ci *			       struct tty_struct *)'
78c2ecf20Sopenharmony_ci *     'int set_selection_kernel(struct tiocl_selection *, struct tty_struct *)'
88c2ecf20Sopenharmony_ci *     'void clear_selection(void)'
98c2ecf20Sopenharmony_ci *     'int paste_selection(struct tty_struct *)'
108c2ecf20Sopenharmony_ci *     'int sel_loadlut(char __user *)'
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * Now that /dev/vcs exists, most of this can disappear again.
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/tty.h>
178c2ecf20Sopenharmony_ci#include <linux/sched.h>
188c2ecf20Sopenharmony_ci#include <linux/mm.h>
198c2ecf20Sopenharmony_ci#include <linux/mutex.h>
208c2ecf20Sopenharmony_ci#include <linux/slab.h>
218c2ecf20Sopenharmony_ci#include <linux/types.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include <linux/kbd_kern.h>
268c2ecf20Sopenharmony_ci#include <linux/vt_kern.h>
278c2ecf20Sopenharmony_ci#include <linux/consolemap.h>
288c2ecf20Sopenharmony_ci#include <linux/selection.h>
298c2ecf20Sopenharmony_ci#include <linux/tiocl.h>
308c2ecf20Sopenharmony_ci#include <linux/console.h>
318c2ecf20Sopenharmony_ci#include <linux/tty_flip.h>
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/* Don't take this from <ctype.h>: 011-015 on the screen aren't spaces */
368c2ecf20Sopenharmony_ci#define isspace(c)	((c) == ' ')
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/* FIXME: all this needs locking */
398c2ecf20Sopenharmony_cistatic struct vc_selection {
408c2ecf20Sopenharmony_ci	struct mutex lock;
418c2ecf20Sopenharmony_ci	struct vc_data *cons;			/* must not be deallocated */
428c2ecf20Sopenharmony_ci	char *buffer;
438c2ecf20Sopenharmony_ci	unsigned int buf_len;
448c2ecf20Sopenharmony_ci	volatile int start;			/* cleared by clear_selection */
458c2ecf20Sopenharmony_ci	int end;
468c2ecf20Sopenharmony_ci} vc_sel = {
478c2ecf20Sopenharmony_ci	.lock = __MUTEX_INITIALIZER(vc_sel.lock),
488c2ecf20Sopenharmony_ci	.start = -1,
498c2ecf20Sopenharmony_ci};
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci/* clear_selection, highlight and highlight_pointer can be called
528c2ecf20Sopenharmony_ci   from interrupt (via scrollback/front) */
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci/* set reverse video on characters s-e of console with selection. */
558c2ecf20Sopenharmony_cistatic inline void highlight(const int s, const int e)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	invert_screen(vc_sel.cons, s, e-s+2, true);
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci/* use complementary color to show the pointer */
618c2ecf20Sopenharmony_cistatic inline void highlight_pointer(const int where)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	complement_pos(vc_sel.cons, where);
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic u32
678c2ecf20Sopenharmony_cisel_pos(int n, bool unicode)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	if (unicode)
708c2ecf20Sopenharmony_ci		return screen_glyph_unicode(vc_sel.cons, n / 2);
718c2ecf20Sopenharmony_ci	return inverse_translate(vc_sel.cons, screen_glyph(vc_sel.cons, n), 0);
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci/**
758c2ecf20Sopenharmony_ci *	clear_selection		-	remove current selection
768c2ecf20Sopenharmony_ci *
778c2ecf20Sopenharmony_ci *	Remove the current selection highlight, if any from the console
788c2ecf20Sopenharmony_ci *	holding the selection. The caller must hold the console lock.
798c2ecf20Sopenharmony_ci */
808c2ecf20Sopenharmony_civoid clear_selection(void)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	highlight_pointer(-1); /* hide the pointer */
838c2ecf20Sopenharmony_ci	if (vc_sel.start != -1) {
848c2ecf20Sopenharmony_ci		highlight(vc_sel.start, vc_sel.end);
858c2ecf20Sopenharmony_ci		vc_sel.start = -1;
868c2ecf20Sopenharmony_ci	}
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(clear_selection);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cibool vc_is_sel(struct vc_data *vc)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	return vc == vc_sel.cons;
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci/*
968c2ecf20Sopenharmony_ci * User settable table: what characters are to be considered alphabetic?
978c2ecf20Sopenharmony_ci * 128 bits. Locked by the console lock.
988c2ecf20Sopenharmony_ci */
998c2ecf20Sopenharmony_cistatic u32 inwordLut[]={
1008c2ecf20Sopenharmony_ci  0x00000000, /* control chars     */
1018c2ecf20Sopenharmony_ci  0x03FFE000, /* digits and "-./"  */
1028c2ecf20Sopenharmony_ci  0x87FFFFFE, /* uppercase and '_' */
1038c2ecf20Sopenharmony_ci  0x07FFFFFE, /* lowercase         */
1048c2ecf20Sopenharmony_ci};
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic inline int inword(const u32 c)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	return c > 0x7f || (( inwordLut[c>>5] >> (c & 0x1F) ) & 1);
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci/**
1128c2ecf20Sopenharmony_ci *	set loadlut		-	load the LUT table
1138c2ecf20Sopenharmony_ci *	@p: user table
1148c2ecf20Sopenharmony_ci *
1158c2ecf20Sopenharmony_ci *	Load the LUT table from user space. The caller must hold the console
1168c2ecf20Sopenharmony_ci *	lock. Make a temporary copy so a partial update doesn't make a mess.
1178c2ecf20Sopenharmony_ci */
1188c2ecf20Sopenharmony_ciint sel_loadlut(char __user *p)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	u32 tmplut[ARRAY_SIZE(inwordLut)];
1218c2ecf20Sopenharmony_ci	if (copy_from_user(tmplut, (u32 __user *)(p+4), sizeof(inwordLut)))
1228c2ecf20Sopenharmony_ci		return -EFAULT;
1238c2ecf20Sopenharmony_ci	memcpy(inwordLut, tmplut, sizeof(inwordLut));
1248c2ecf20Sopenharmony_ci	return 0;
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci/* does screen address p correspond to character at LH/RH edge of screen? */
1288c2ecf20Sopenharmony_cistatic inline int atedge(const int p, int size_row)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	return (!(p % size_row)	|| !((p + 2) % size_row));
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci/* stores the char in UTF8 and returns the number of bytes used (1-4) */
1348c2ecf20Sopenharmony_cistatic int store_utf8(u32 c, char *p)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	if (c < 0x80) {
1378c2ecf20Sopenharmony_ci		/*  0******* */
1388c2ecf20Sopenharmony_ci		p[0] = c;
1398c2ecf20Sopenharmony_ci		return 1;
1408c2ecf20Sopenharmony_ci	} else if (c < 0x800) {
1418c2ecf20Sopenharmony_ci		/* 110***** 10****** */
1428c2ecf20Sopenharmony_ci		p[0] = 0xc0 | (c >> 6);
1438c2ecf20Sopenharmony_ci		p[1] = 0x80 | (c & 0x3f);
1448c2ecf20Sopenharmony_ci		return 2;
1458c2ecf20Sopenharmony_ci	} else if (c < 0x10000) {
1468c2ecf20Sopenharmony_ci		/* 1110**** 10****** 10****** */
1478c2ecf20Sopenharmony_ci		p[0] = 0xe0 | (c >> 12);
1488c2ecf20Sopenharmony_ci		p[1] = 0x80 | ((c >> 6) & 0x3f);
1498c2ecf20Sopenharmony_ci		p[2] = 0x80 | (c & 0x3f);
1508c2ecf20Sopenharmony_ci		return 3;
1518c2ecf20Sopenharmony_ci	} else if (c < 0x110000) {
1528c2ecf20Sopenharmony_ci		/* 11110*** 10****** 10****** 10****** */
1538c2ecf20Sopenharmony_ci		p[0] = 0xf0 | (c >> 18);
1548c2ecf20Sopenharmony_ci		p[1] = 0x80 | ((c >> 12) & 0x3f);
1558c2ecf20Sopenharmony_ci		p[2] = 0x80 | ((c >> 6) & 0x3f);
1568c2ecf20Sopenharmony_ci		p[3] = 0x80 | (c & 0x3f);
1578c2ecf20Sopenharmony_ci		return 4;
1588c2ecf20Sopenharmony_ci	} else {
1598c2ecf20Sopenharmony_ci		/* outside Unicode, replace with U+FFFD */
1608c2ecf20Sopenharmony_ci		p[0] = 0xef;
1618c2ecf20Sopenharmony_ci		p[1] = 0xbf;
1628c2ecf20Sopenharmony_ci		p[2] = 0xbd;
1638c2ecf20Sopenharmony_ci		return 3;
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci/**
1688c2ecf20Sopenharmony_ci *	set_selection_user	-	set the current selection.
1698c2ecf20Sopenharmony_ci *	@sel: user selection info
1708c2ecf20Sopenharmony_ci *	@tty: the console tty
1718c2ecf20Sopenharmony_ci *
1728c2ecf20Sopenharmony_ci *	Invoked by the ioctl handle for the vt layer.
1738c2ecf20Sopenharmony_ci *
1748c2ecf20Sopenharmony_ci *	The entire selection process is managed under the console_lock. It's
1758c2ecf20Sopenharmony_ci *	 a lot under the lock but its hardly a performance path
1768c2ecf20Sopenharmony_ci */
1778c2ecf20Sopenharmony_ciint set_selection_user(const struct tiocl_selection __user *sel,
1788c2ecf20Sopenharmony_ci		       struct tty_struct *tty)
1798c2ecf20Sopenharmony_ci{
1808c2ecf20Sopenharmony_ci	struct tiocl_selection v;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	if (copy_from_user(&v, sel, sizeof(*sel)))
1838c2ecf20Sopenharmony_ci		return -EFAULT;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	return set_selection_kernel(&v, tty);
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic int vc_selection_store_chars(struct vc_data *vc, bool unicode)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	char *bp, *obp;
1918c2ecf20Sopenharmony_ci	unsigned int i;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	/* Allocate a new buffer before freeing the old one ... */
1948c2ecf20Sopenharmony_ci	/* chars can take up to 4 bytes with unicode */
1958c2ecf20Sopenharmony_ci	bp = kmalloc_array((vc_sel.end - vc_sel.start) / 2 + 1, unicode ? 4 : 1,
1968c2ecf20Sopenharmony_ci			   GFP_KERNEL | __GFP_NOWARN);
1978c2ecf20Sopenharmony_ci	if (!bp) {
1988c2ecf20Sopenharmony_ci		printk(KERN_WARNING "selection: kmalloc() failed\n");
1998c2ecf20Sopenharmony_ci		clear_selection();
2008c2ecf20Sopenharmony_ci		return -ENOMEM;
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci	kfree(vc_sel.buffer);
2038c2ecf20Sopenharmony_ci	vc_sel.buffer = bp;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	obp = bp;
2068c2ecf20Sopenharmony_ci	for (i = vc_sel.start; i <= vc_sel.end; i += 2) {
2078c2ecf20Sopenharmony_ci		u32 c = sel_pos(i, unicode);
2088c2ecf20Sopenharmony_ci		if (unicode)
2098c2ecf20Sopenharmony_ci			bp += store_utf8(c, bp);
2108c2ecf20Sopenharmony_ci		else
2118c2ecf20Sopenharmony_ci			*bp++ = c;
2128c2ecf20Sopenharmony_ci		if (!isspace(c))
2138c2ecf20Sopenharmony_ci			obp = bp;
2148c2ecf20Sopenharmony_ci		if (!((i + 2) % vc->vc_size_row)) {
2158c2ecf20Sopenharmony_ci			/* strip trailing blanks from line and add newline,
2168c2ecf20Sopenharmony_ci			   unless non-space at end of line. */
2178c2ecf20Sopenharmony_ci			if (obp != bp) {
2188c2ecf20Sopenharmony_ci				bp = obp;
2198c2ecf20Sopenharmony_ci				*bp++ = '\r';
2208c2ecf20Sopenharmony_ci			}
2218c2ecf20Sopenharmony_ci			obp = bp;
2228c2ecf20Sopenharmony_ci		}
2238c2ecf20Sopenharmony_ci	}
2248c2ecf20Sopenharmony_ci	vc_sel.buf_len = bp - vc_sel.buffer;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	return 0;
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic int vc_do_selection(struct vc_data *vc, unsigned short mode, int ps,
2308c2ecf20Sopenharmony_ci		int pe)
2318c2ecf20Sopenharmony_ci{
2328c2ecf20Sopenharmony_ci	int new_sel_start, new_sel_end, spc;
2338c2ecf20Sopenharmony_ci	bool unicode = vt_do_kdgkbmode(fg_console) == K_UNICODE;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	switch (mode) {
2368c2ecf20Sopenharmony_ci	case TIOCL_SELCHAR:	/* character-by-character selection */
2378c2ecf20Sopenharmony_ci		new_sel_start = ps;
2388c2ecf20Sopenharmony_ci		new_sel_end = pe;
2398c2ecf20Sopenharmony_ci		break;
2408c2ecf20Sopenharmony_ci	case TIOCL_SELWORD:	/* word-by-word selection */
2418c2ecf20Sopenharmony_ci		spc = isspace(sel_pos(ps, unicode));
2428c2ecf20Sopenharmony_ci		for (new_sel_start = ps; ; ps -= 2) {
2438c2ecf20Sopenharmony_ci			if ((spc && !isspace(sel_pos(ps, unicode))) ||
2448c2ecf20Sopenharmony_ci			    (!spc && !inword(sel_pos(ps, unicode))))
2458c2ecf20Sopenharmony_ci				break;
2468c2ecf20Sopenharmony_ci			new_sel_start = ps;
2478c2ecf20Sopenharmony_ci			if (!(ps % vc->vc_size_row))
2488c2ecf20Sopenharmony_ci				break;
2498c2ecf20Sopenharmony_ci		}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci		spc = isspace(sel_pos(pe, unicode));
2528c2ecf20Sopenharmony_ci		for (new_sel_end = pe; ; pe += 2) {
2538c2ecf20Sopenharmony_ci			if ((spc && !isspace(sel_pos(pe, unicode))) ||
2548c2ecf20Sopenharmony_ci			    (!spc && !inword(sel_pos(pe, unicode))))
2558c2ecf20Sopenharmony_ci				break;
2568c2ecf20Sopenharmony_ci			new_sel_end = pe;
2578c2ecf20Sopenharmony_ci			if (!((pe + 2) % vc->vc_size_row))
2588c2ecf20Sopenharmony_ci				break;
2598c2ecf20Sopenharmony_ci		}
2608c2ecf20Sopenharmony_ci		break;
2618c2ecf20Sopenharmony_ci	case TIOCL_SELLINE:	/* line-by-line selection */
2628c2ecf20Sopenharmony_ci		new_sel_start = rounddown(ps, vc->vc_size_row);
2638c2ecf20Sopenharmony_ci		new_sel_end = rounddown(pe, vc->vc_size_row) +
2648c2ecf20Sopenharmony_ci			vc->vc_size_row - 2;
2658c2ecf20Sopenharmony_ci		break;
2668c2ecf20Sopenharmony_ci	case TIOCL_SELPOINTER:
2678c2ecf20Sopenharmony_ci		highlight_pointer(pe);
2688c2ecf20Sopenharmony_ci		return 0;
2698c2ecf20Sopenharmony_ci	default:
2708c2ecf20Sopenharmony_ci		return -EINVAL;
2718c2ecf20Sopenharmony_ci	}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	/* remove the pointer */
2748c2ecf20Sopenharmony_ci	highlight_pointer(-1);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	/* select to end of line if on trailing space */
2778c2ecf20Sopenharmony_ci	if (new_sel_end > new_sel_start &&
2788c2ecf20Sopenharmony_ci		!atedge(new_sel_end, vc->vc_size_row) &&
2798c2ecf20Sopenharmony_ci		isspace(sel_pos(new_sel_end, unicode))) {
2808c2ecf20Sopenharmony_ci		for (pe = new_sel_end + 2; ; pe += 2)
2818c2ecf20Sopenharmony_ci			if (!isspace(sel_pos(pe, unicode)) ||
2828c2ecf20Sopenharmony_ci			    atedge(pe, vc->vc_size_row))
2838c2ecf20Sopenharmony_ci				break;
2848c2ecf20Sopenharmony_ci		if (isspace(sel_pos(pe, unicode)))
2858c2ecf20Sopenharmony_ci			new_sel_end = pe;
2868c2ecf20Sopenharmony_ci	}
2878c2ecf20Sopenharmony_ci	if (vc_sel.start == -1)	/* no current selection */
2888c2ecf20Sopenharmony_ci		highlight(new_sel_start, new_sel_end);
2898c2ecf20Sopenharmony_ci	else if (new_sel_start == vc_sel.start)
2908c2ecf20Sopenharmony_ci	{
2918c2ecf20Sopenharmony_ci		if (new_sel_end == vc_sel.end)	/* no action required */
2928c2ecf20Sopenharmony_ci			return 0;
2938c2ecf20Sopenharmony_ci		else if (new_sel_end > vc_sel.end)	/* extend to right */
2948c2ecf20Sopenharmony_ci			highlight(vc_sel.end + 2, new_sel_end);
2958c2ecf20Sopenharmony_ci		else				/* contract from right */
2968c2ecf20Sopenharmony_ci			highlight(new_sel_end + 2, vc_sel.end);
2978c2ecf20Sopenharmony_ci	}
2988c2ecf20Sopenharmony_ci	else if (new_sel_end == vc_sel.end)
2998c2ecf20Sopenharmony_ci	{
3008c2ecf20Sopenharmony_ci		if (new_sel_start < vc_sel.start) /* extend to left */
3018c2ecf20Sopenharmony_ci			highlight(new_sel_start, vc_sel.start - 2);
3028c2ecf20Sopenharmony_ci		else				/* contract from left */
3038c2ecf20Sopenharmony_ci			highlight(vc_sel.start, new_sel_start - 2);
3048c2ecf20Sopenharmony_ci	}
3058c2ecf20Sopenharmony_ci	else	/* some other case; start selection from scratch */
3068c2ecf20Sopenharmony_ci	{
3078c2ecf20Sopenharmony_ci		clear_selection();
3088c2ecf20Sopenharmony_ci		highlight(new_sel_start, new_sel_end);
3098c2ecf20Sopenharmony_ci	}
3108c2ecf20Sopenharmony_ci	vc_sel.start = new_sel_start;
3118c2ecf20Sopenharmony_ci	vc_sel.end = new_sel_end;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	return vc_selection_store_chars(vc, unicode);
3148c2ecf20Sopenharmony_ci}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_cistatic int vc_selection(struct vc_data *vc, struct tiocl_selection *v,
3178c2ecf20Sopenharmony_ci		struct tty_struct *tty)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	int ps, pe;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	poke_blanked_console();
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	if (v->sel_mode == TIOCL_SELCLEAR) {
3248c2ecf20Sopenharmony_ci		/* useful for screendump without selection highlights */
3258c2ecf20Sopenharmony_ci		clear_selection();
3268c2ecf20Sopenharmony_ci		return 0;
3278c2ecf20Sopenharmony_ci	}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	v->xs = min_t(u16, v->xs - 1, vc->vc_cols - 1);
3308c2ecf20Sopenharmony_ci	v->ys = min_t(u16, v->ys - 1, vc->vc_rows - 1);
3318c2ecf20Sopenharmony_ci	v->xe = min_t(u16, v->xe - 1, vc->vc_cols - 1);
3328c2ecf20Sopenharmony_ci	v->ye = min_t(u16, v->ye - 1, vc->vc_rows - 1);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	if (mouse_reporting() && (v->sel_mode & TIOCL_SELMOUSEREPORT)) {
3358c2ecf20Sopenharmony_ci		mouse_report(tty, v->sel_mode & TIOCL_SELBUTTONMASK, v->xs,
3368c2ecf20Sopenharmony_ci			     v->ys);
3378c2ecf20Sopenharmony_ci		return 0;
3388c2ecf20Sopenharmony_ci	}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	ps = v->ys * vc->vc_size_row + (v->xs << 1);
3418c2ecf20Sopenharmony_ci	pe = v->ye * vc->vc_size_row + (v->xe << 1);
3428c2ecf20Sopenharmony_ci	if (ps > pe)	/* make vc_sel.start <= vc_sel.end */
3438c2ecf20Sopenharmony_ci		swap(ps, pe);
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	if (vc_sel.cons != vc) {
3468c2ecf20Sopenharmony_ci		clear_selection();
3478c2ecf20Sopenharmony_ci		vc_sel.cons = vc;
3488c2ecf20Sopenharmony_ci	}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	return vc_do_selection(vc, v->sel_mode, ps, pe);
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ciint set_selection_kernel(struct tiocl_selection *v, struct tty_struct *tty)
3548c2ecf20Sopenharmony_ci{
3558c2ecf20Sopenharmony_ci	int ret;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	mutex_lock(&vc_sel.lock);
3588c2ecf20Sopenharmony_ci	console_lock();
3598c2ecf20Sopenharmony_ci	ret = vc_selection(vc_cons[fg_console].d, v, tty);
3608c2ecf20Sopenharmony_ci	console_unlock();
3618c2ecf20Sopenharmony_ci	mutex_unlock(&vc_sel.lock);
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	return ret;
3648c2ecf20Sopenharmony_ci}
3658c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(set_selection_kernel);
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci/* Insert the contents of the selection buffer into the
3688c2ecf20Sopenharmony_ci * queue of the tty associated with the current console.
3698c2ecf20Sopenharmony_ci * Invoked by ioctl().
3708c2ecf20Sopenharmony_ci *
3718c2ecf20Sopenharmony_ci * Locking: called without locks. Calls the ldisc wrongly with
3728c2ecf20Sopenharmony_ci * unsafe methods,
3738c2ecf20Sopenharmony_ci */
3748c2ecf20Sopenharmony_ciint paste_selection(struct tty_struct *tty)
3758c2ecf20Sopenharmony_ci{
3768c2ecf20Sopenharmony_ci	struct vc_data *vc = tty->driver_data;
3778c2ecf20Sopenharmony_ci	int	pasted = 0;
3788c2ecf20Sopenharmony_ci	unsigned int count;
3798c2ecf20Sopenharmony_ci	struct  tty_ldisc *ld;
3808c2ecf20Sopenharmony_ci	DECLARE_WAITQUEUE(wait, current);
3818c2ecf20Sopenharmony_ci	int ret = 0;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	console_lock();
3848c2ecf20Sopenharmony_ci	poke_blanked_console();
3858c2ecf20Sopenharmony_ci	console_unlock();
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	ld = tty_ldisc_ref_wait(tty);
3888c2ecf20Sopenharmony_ci	if (!ld)
3898c2ecf20Sopenharmony_ci		return -EIO;	/* ldisc was hung up */
3908c2ecf20Sopenharmony_ci	tty_buffer_lock_exclusive(&vc->port);
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	add_wait_queue(&vc->paste_wait, &wait);
3938c2ecf20Sopenharmony_ci	mutex_lock(&vc_sel.lock);
3948c2ecf20Sopenharmony_ci	while (vc_sel.buffer && vc_sel.buf_len > pasted) {
3958c2ecf20Sopenharmony_ci		set_current_state(TASK_INTERRUPTIBLE);
3968c2ecf20Sopenharmony_ci		if (signal_pending(current)) {
3978c2ecf20Sopenharmony_ci			ret = -EINTR;
3988c2ecf20Sopenharmony_ci			break;
3998c2ecf20Sopenharmony_ci		}
4008c2ecf20Sopenharmony_ci		if (tty_throttled(tty)) {
4018c2ecf20Sopenharmony_ci			mutex_unlock(&vc_sel.lock);
4028c2ecf20Sopenharmony_ci			schedule();
4038c2ecf20Sopenharmony_ci			mutex_lock(&vc_sel.lock);
4048c2ecf20Sopenharmony_ci			continue;
4058c2ecf20Sopenharmony_ci		}
4068c2ecf20Sopenharmony_ci		__set_current_state(TASK_RUNNING);
4078c2ecf20Sopenharmony_ci		count = vc_sel.buf_len - pasted;
4088c2ecf20Sopenharmony_ci		count = tty_ldisc_receive_buf(ld, vc_sel.buffer + pasted, NULL,
4098c2ecf20Sopenharmony_ci					      count);
4108c2ecf20Sopenharmony_ci		pasted += count;
4118c2ecf20Sopenharmony_ci	}
4128c2ecf20Sopenharmony_ci	mutex_unlock(&vc_sel.lock);
4138c2ecf20Sopenharmony_ci	remove_wait_queue(&vc->paste_wait, &wait);
4148c2ecf20Sopenharmony_ci	__set_current_state(TASK_RUNNING);
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	tty_buffer_unlock_exclusive(&vc->port);
4178c2ecf20Sopenharmony_ci	tty_ldisc_deref(ld);
4188c2ecf20Sopenharmony_ci	return ret;
4198c2ecf20Sopenharmony_ci}
4208c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(paste_selection);
421