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