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