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