blob: da8fb9baca1d90c747f3c84918866689ba0e5014 [file] [log] [blame]
xjb04a4022021-11-25 15:01:52 +08001// SPDX-License-Identifier: GPL-2.0
2/*
3 * This module exports the functions:
4 *
5 * 'int set_selection(struct tiocl_selection __user *, struct tty_struct *)'
6 * 'void clear_selection(void)'
7 * 'int paste_selection(struct tty_struct *)'
8 * 'int sel_loadlut(char __user *)'
9 *
10 * Now that /dev/vcs exists, most of this can disappear again.
11 */
12
13#include <linux/module.h>
14#include <linux/tty.h>
15#include <linux/sched.h>
16#include <linux/mm.h>
17#include <linux/mutex.h>
18#include <linux/slab.h>
19#include <linux/types.h>
20
21#include <linux/uaccess.h>
22
23#include <linux/kbd_kern.h>
24#include <linux/vt_kern.h>
25#include <linux/consolemap.h>
26#include <linux/selection.h>
27#include <linux/tiocl.h>
28#include <linux/console.h>
29#include <linux/tty_flip.h>
30
31/* Don't take this from <ctype.h>: 011-015 on the screen aren't spaces */
32#define isspace(c) ((c) == ' ')
33
34extern void poke_blanked_console(void);
35
36/* FIXME: all this needs locking */
37/* Variables for selection control. */
38/* Use a dynamic buffer, instead of static (Dec 1994) */
39struct vc_data *sel_cons; /* must not be deallocated */
40static int use_unicode;
41static volatile int sel_start = -1; /* cleared by clear_selection */
42static int sel_end;
43static int sel_buffer_lth;
44static char *sel_buffer;
45static DEFINE_MUTEX(sel_lock);
46
47/* clear_selection, highlight and highlight_pointer can be called
48 from interrupt (via scrollback/front) */
49
50/* set reverse video on characters s-e of console with selection. */
51static inline void highlight(const int s, const int e)
52{
53 invert_screen(sel_cons, s, e-s+2, 1);
54}
55
56/* use complementary color to show the pointer */
57static inline void highlight_pointer(const int where)
58{
59 complement_pos(sel_cons, where);
60}
61
62static u32
63sel_pos(int n)
64{
65 if (use_unicode)
66 return screen_glyph_unicode(sel_cons, n / 2);
67 return inverse_translate(sel_cons, screen_glyph(sel_cons, n),
68 0);
69}
70
71/**
72 * clear_selection - remove current selection
73 *
74 * Remove the current selection highlight, if any from the console
75 * holding the selection. The caller must hold the console lock.
76 */
77void clear_selection(void)
78{
79 highlight_pointer(-1); /* hide the pointer */
80 if (sel_start != -1) {
81 highlight(sel_start, sel_end);
82 sel_start = -1;
83 }
84}
85
86/*
87 * User settable table: what characters are to be considered alphabetic?
88 * 128 bits. Locked by the console lock.
89 */
90static u32 inwordLut[]={
91 0x00000000, /* control chars */
92 0x03FFE000, /* digits and "-./" */
93 0x87FFFFFE, /* uppercase and '_' */
94 0x07FFFFFE, /* lowercase */
95};
96
97static inline int inword(const u32 c)
98{
99 return c > 0x7f || (( inwordLut[c>>5] >> (c & 0x1F) ) & 1);
100}
101
102/**
103 * set loadlut - load the LUT table
104 * @p: user table
105 *
106 * Load the LUT table from user space. The caller must hold the console
107 * lock. Make a temporary copy so a partial update doesn't make a mess.
108 */
109int sel_loadlut(char __user *p)
110{
111 u32 tmplut[ARRAY_SIZE(inwordLut)];
112 if (copy_from_user(tmplut, (u32 __user *)(p+4), sizeof(inwordLut)))
113 return -EFAULT;
114 memcpy(inwordLut, tmplut, sizeof(inwordLut));
115 return 0;
116}
117
118/* does screen address p correspond to character at LH/RH edge of screen? */
119static inline int atedge(const int p, int size_row)
120{
121 return (!(p % size_row) || !((p + 2) % size_row));
122}
123
124/* stores the char in UTF8 and returns the number of bytes used (1-4) */
125static int store_utf8(u32 c, char *p)
126{
127 if (c < 0x80) {
128 /* 0******* */
129 p[0] = c;
130 return 1;
131 } else if (c < 0x800) {
132 /* 110***** 10****** */
133 p[0] = 0xc0 | (c >> 6);
134 p[1] = 0x80 | (c & 0x3f);
135 return 2;
136 } else if (c < 0x10000) {
137 /* 1110**** 10****** 10****** */
138 p[0] = 0xe0 | (c >> 12);
139 p[1] = 0x80 | ((c >> 6) & 0x3f);
140 p[2] = 0x80 | (c & 0x3f);
141 return 3;
142 } else if (c < 0x110000) {
143 /* 11110*** 10****** 10****** 10****** */
144 p[0] = 0xf0 | (c >> 18);
145 p[1] = 0x80 | ((c >> 12) & 0x3f);
146 p[2] = 0x80 | ((c >> 6) & 0x3f);
147 p[3] = 0x80 | (c & 0x3f);
148 return 4;
149 } else {
150 /* outside Unicode, replace with U+FFFD */
151 p[0] = 0xef;
152 p[1] = 0xbf;
153 p[2] = 0xbd;
154 return 3;
155 }
156}
157
158/**
159 * set_selection - set the current selection.
160 * @sel: user selection info
161 * @tty: the console tty
162 *
163 * Invoked by the ioctl handle for the vt layer.
164 *
165 * The entire selection process is managed under the console_lock. It's
166 * a lot under the lock but its hardly a performance path
167 */
168static int __set_selection(const struct tiocl_selection __user *sel,
169 struct tty_struct *tty)
170{
171 struct vc_data *vc = vc_cons[fg_console].d;
172 int new_sel_start, new_sel_end, spc;
173 struct tiocl_selection v;
174 char *bp, *obp;
175 int i, ps, pe, multiplier;
176 u16 c;
177 int mode, ret = 0;
178
179 poke_blanked_console();
180 if (copy_from_user(&v, sel, sizeof(*sel)))
181 return -EFAULT;
182
183 v.xs = min_t(u16, v.xs - 1, vc->vc_cols - 1);
184 v.ys = min_t(u16, v.ys - 1, vc->vc_rows - 1);
185 v.xe = min_t(u16, v.xe - 1, vc->vc_cols - 1);
186 v.ye = min_t(u16, v.ye - 1, vc->vc_rows - 1);
187 ps = v.ys * vc->vc_size_row + (v.xs << 1);
188 pe = v.ye * vc->vc_size_row + (v.xe << 1);
189
190 if (v.sel_mode == TIOCL_SELCLEAR) {
191 /* useful for screendump without selection highlights */
192 clear_selection();
193 return 0;
194 }
195
196 if (mouse_reporting() && (v.sel_mode & TIOCL_SELMOUSEREPORT)) {
197 mouse_report(tty, v.sel_mode & TIOCL_SELBUTTONMASK, v.xs, v.ys);
198 return 0;
199 }
200
201 if (ps > pe) /* make sel_start <= sel_end */
202 swap(ps, pe);
203
204 if (sel_cons != vc_cons[fg_console].d) {
205 clear_selection();
206 sel_cons = vc_cons[fg_console].d;
207 }
208 mode = vt_do_kdgkbmode(fg_console);
209 if (mode == K_UNICODE)
210 use_unicode = 1;
211 else
212 use_unicode = 0;
213
214 switch (v.sel_mode)
215 {
216 case TIOCL_SELCHAR: /* character-by-character selection */
217 new_sel_start = ps;
218 new_sel_end = pe;
219 break;
220 case TIOCL_SELWORD: /* word-by-word selection */
221 spc = isspace(sel_pos(ps));
222 for (new_sel_start = ps; ; ps -= 2)
223 {
224 if ((spc && !isspace(sel_pos(ps))) ||
225 (!spc && !inword(sel_pos(ps))))
226 break;
227 new_sel_start = ps;
228 if (!(ps % vc->vc_size_row))
229 break;
230 }
231 spc = isspace(sel_pos(pe));
232 for (new_sel_end = pe; ; pe += 2)
233 {
234 if ((spc && !isspace(sel_pos(pe))) ||
235 (!spc && !inword(sel_pos(pe))))
236 break;
237 new_sel_end = pe;
238 if (!((pe + 2) % vc->vc_size_row))
239 break;
240 }
241 break;
242 case TIOCL_SELLINE: /* line-by-line selection */
243 new_sel_start = ps - ps % vc->vc_size_row;
244 new_sel_end = pe + vc->vc_size_row
245 - pe % vc->vc_size_row - 2;
246 break;
247 case TIOCL_SELPOINTER:
248 highlight_pointer(pe);
249 return 0;
250 default:
251 return -EINVAL;
252 }
253
254 /* remove the pointer */
255 highlight_pointer(-1);
256
257 /* select to end of line if on trailing space */
258 if (new_sel_end > new_sel_start &&
259 !atedge(new_sel_end, vc->vc_size_row) &&
260 isspace(sel_pos(new_sel_end))) {
261 for (pe = new_sel_end + 2; ; pe += 2)
262 if (!isspace(sel_pos(pe)) ||
263 atedge(pe, vc->vc_size_row))
264 break;
265 if (isspace(sel_pos(pe)))
266 new_sel_end = pe;
267 }
268 if (sel_start == -1) /* no current selection */
269 highlight(new_sel_start, new_sel_end);
270 else if (new_sel_start == sel_start)
271 {
272 if (new_sel_end == sel_end) /* no action required */
273 return 0;
274 else if (new_sel_end > sel_end) /* extend to right */
275 highlight(sel_end + 2, new_sel_end);
276 else /* contract from right */
277 highlight(new_sel_end + 2, sel_end);
278 }
279 else if (new_sel_end == sel_end)
280 {
281 if (new_sel_start < sel_start) /* extend to left */
282 highlight(new_sel_start, sel_start - 2);
283 else /* contract from left */
284 highlight(sel_start, new_sel_start - 2);
285 }
286 else /* some other case; start selection from scratch */
287 {
288 clear_selection();
289 highlight(new_sel_start, new_sel_end);
290 }
291 sel_start = new_sel_start;
292 sel_end = new_sel_end;
293
294 /* Allocate a new buffer before freeing the old one ... */
295 multiplier = use_unicode ? 4 : 1; /* chars can take up to 4 bytes */
296 bp = kmalloc_array((sel_end - sel_start) / 2 + 1, multiplier,
297 GFP_KERNEL);
298 if (!bp) {
299 printk(KERN_WARNING "selection: kmalloc() failed\n");
300 clear_selection();
301 return -ENOMEM;
302 }
303 kfree(sel_buffer);
304 sel_buffer = bp;
305
306 obp = bp;
307 for (i = sel_start; i <= sel_end; i += 2) {
308 c = sel_pos(i);
309 if (use_unicode)
310 bp += store_utf8(c, bp);
311 else
312 *bp++ = c;
313 if (!isspace(c))
314 obp = bp;
315 if (! ((i + 2) % vc->vc_size_row)) {
316 /* strip trailing blanks from line and add newline,
317 unless non-space at end of line. */
318 if (obp != bp) {
319 bp = obp;
320 *bp++ = '\r';
321 }
322 obp = bp;
323 }
324 }
325 sel_buffer_lth = bp - sel_buffer;
326
327 return ret;
328}
329
330int set_selection(const struct tiocl_selection __user *v,
331 struct tty_struct *tty)
332{
333 int ret;
334
335 mutex_lock(&sel_lock);
336 console_lock();
337 ret = __set_selection(v, tty);
338 console_unlock();
339 mutex_unlock(&sel_lock);
340
341 return ret;
342}
343
344/* Insert the contents of the selection buffer into the
345 * queue of the tty associated with the current console.
346 * Invoked by ioctl().
347 *
348 * Locking: called without locks. Calls the ldisc wrongly with
349 * unsafe methods,
350 */
351int paste_selection(struct tty_struct *tty)
352{
353 struct vc_data *vc = tty->driver_data;
354 int pasted = 0;
355 unsigned int count;
356 struct tty_ldisc *ld;
357 DECLARE_WAITQUEUE(wait, current);
358
359 console_lock();
360 poke_blanked_console();
361 console_unlock();
362
363 ld = tty_ldisc_ref_wait(tty);
364 if (!ld)
365 return -EIO; /* ldisc was hung up */
366 tty_buffer_lock_exclusive(&vc->port);
367
368 add_wait_queue(&vc->paste_wait, &wait);
369 mutex_lock(&sel_lock);
370 while (sel_buffer && sel_buffer_lth > pasted) {
371 set_current_state(TASK_INTERRUPTIBLE);
372 if (tty_throttled(tty)) {
373 mutex_unlock(&sel_lock);
374 schedule();
375 mutex_lock(&sel_lock);
376 continue;
377 }
378 __set_current_state(TASK_RUNNING);
379 count = sel_buffer_lth - pasted;
380 count = tty_ldisc_receive_buf(ld, sel_buffer + pasted, NULL,
381 count);
382 pasted += count;
383 }
384 mutex_unlock(&sel_lock);
385 remove_wait_queue(&vc->paste_wait, &wait);
386 __set_current_state(TASK_RUNNING);
387
388 tty_buffer_unlock_exclusive(&vc->port);
389 tty_ldisc_deref(ld);
390 return 0;
391}