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