button.c revision 913cc679
1/* $XTermId: button.c,v 1.524 2017/05/30 08:58:29 tom Exp $ */
2
3/*
4 * Copyright 1999-2016,2017 by Thomas E. Dickey
5 *
6 *                         All Rights Reserved
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the
10 * "Software"), to deal in the Software without restriction, including
11 * without limitation the rights to use, copy, modify, merge, publish,
12 * distribute, sublicense, and/or sell copies of the Software, and to
13 * permit persons to whom the Software is furnished to do so, subject to
14 * the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22 * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
23 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 *
27 * Except as contained in this notice, the name(s) of the above copyright
28 * holders shall not be used in advertising or otherwise to promote the
29 * sale, use or other dealings in this Software without prior written
30 * authorization.
31 *
32 *
33 * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
34 *
35 *                         All Rights Reserved
36 *
37 * Permission to use, copy, modify, and distribute this software and its
38 * documentation for any purpose and without fee is hereby granted,
39 * provided that the above copyright notice appear in all copies and that
40 * both that copyright notice and this permission notice appear in
41 * supporting documentation, and that the name of Digital Equipment
42 * Corporation not be used in advertising or publicity pertaining to
43 * distribution of the software without specific, written prior permission.
44 *
45 *
46 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
47 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
48 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
49 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
50 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
51 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
52 * SOFTWARE.
53 */
54
55/*
56button.c	Handles button events in the terminal emulator.
57		does cut/paste operations, change modes via menu,
58		passes button events through to some applications.
59				J. Gettys.
60*/
61
62#include <xterm.h>
63
64#include <stdio.h>
65#include <ctype.h>
66#include <assert.h>
67
68#include <X11/Xatom.h>
69#include <X11/Xmu/Atoms.h>
70#include <X11/Xmu/StdSel.h>
71
72#include <xutf8.h>
73#include <fontutils.h>
74
75#include <data.h>
76#include <error.h>
77#include <menu.h>
78#include <charclass.h>
79#include <xstrings.h>
80
81#if OPT_SELECT_REGEX
82#ifdef HAVE_PCREPOSIX_H
83#include <pcreposix.h>
84#else /* POSIX regex.h */
85#include <sys/types.h>
86#include <regex.h>
87#endif
88#endif
89
90#if OPT_WIDE_CHARS
91#include <ctype.h>
92#include <wcwidth.h>
93#else
94#define CharacterClass(value) \
95	charClass[(value) & ((sizeof(charClass)/sizeof(charClass[0]))-1)]
96#endif
97
98    /*
99     * We'll generally map rows to indices when doing selection.
100     * Simplify that with a macro.
101     *
102     * Note that ROW2INX() is safe to use with auto increment/decrement for
103     * the row expression since that is evaluated once.
104     */
105#define GET_LINEDATA(screen, row) \
106	getLineData(screen, ROW2INX(screen, row))
107
108    /*
109     * We reserve shift modifier for cut/paste operations.  In principle we
110     * can pass through control and meta modifiers, but in practice, the
111     * popup menu uses control, and the window manager is likely to use meta,
112     * so those events are not delivered to SendMousePosition.
113     */
114#define OurModifiers (ShiftMask | ControlMask | Mod1Mask)
115#define AllModifiers (ShiftMask | LockMask | ControlMask | Mod1Mask | \
116		      Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask)
117
118#define BtnModifiers(event) (event->state & OurModifiers)
119#define KeyModifiers(event) (event->xbutton.state & OurModifiers)
120
121#define IsBtnEvent(event) ((event)->type == ButtonPress || (event)->type == ButtonRelease)
122#define IsKeyEvent(event) ((event)->type == KeyPress    || (event)->type == KeyRelease)
123
124#define KeyState(x) (((int) ((x) & (ShiftMask|ControlMask))) \
125			  + (((x) & Mod1Mask) ? 2 : 0))
126    /* adds together the bits:
127       shift key -> 1
128       meta key  -> 2
129       control key -> 4 */
130
131#define	Coordinate(s,c)	((c)->row * MaxCols(s) + (c)->col)
132
133static const CELL zeroCELL =
134{0, 0};
135
136#if OPT_DEC_LOCATOR
137static Bool SendLocatorPosition(XtermWidget xw, XButtonEvent *event);
138static void CheckLocatorPosition(XtermWidget xw, XButtonEvent *event);
139#endif /* OPT_DEC_LOCATOR */
140
141/* Multi-click handling */
142#if OPT_READLINE
143static Time lastButtonDownTime = 0;
144static int ExtendingSelection = 0;
145static Time lastButton3UpTime = 0;
146static Time lastButton3DoubleDownTime = 0;
147static CELL lastButton3;	/* At the release time */
148#endif /* OPT_READLINE */
149
150static Char *SaveText(TScreen *screen, int row, int scol, int ecol,
151		      Char *lp, int *eol);
152static int Length(TScreen *screen, int row, int scol, int ecol);
153static void ComputeSelect(XtermWidget xw, CELL *startc, CELL *endc, Bool extend);
154static void EditorButton(XtermWidget xw, XButtonEvent *event);
155static void EndExtend(XtermWidget w, XEvent *event, String *params, Cardinal
156		      num_params, Bool use_cursor_loc);
157static void ExtendExtend(XtermWidget xw, const CELL *cell);
158static void PointToCELL(TScreen *screen, int y, int x, CELL *cell);
159static void ReHiliteText(XtermWidget xw, CELL *first, CELL *last);
160static void SaltTextAway(XtermWidget xw, CELL *cellc, CELL *cell);
161static void SelectSet(XtermWidget xw, XEvent *event, String *params, Cardinal num_params);
162static void SelectionReceived PROTO_XT_SEL_CB_ARGS;
163static void StartSelect(XtermWidget xw, const CELL *cell);
164static void TrackDown(XtermWidget xw, XButtonEvent *event);
165static void TrackText(XtermWidget xw, const CELL *first, const CELL *last);
166static void _OwnSelection(XtermWidget xw, String *selections, Cardinal count);
167static void do_select_end(XtermWidget xw, XEvent *event, String *params,
168			  Cardinal *num_params, Bool use_cursor_loc);
169
170#define MOUSE_LIMIT (255 - 32)
171
172/* Send SET_EXT_SIZE_MOUSE to enable offsets up to EXT_MOUSE_LIMIT */
173#define EXT_MOUSE_LIMIT (2047 - 32)
174#define EXT_MOUSE_START (127 - 32)
175
176static int
177MouseLimit(TScreen *screen)
178{
179    int mouse_limit;
180
181    switch (screen->extend_coords) {
182    default:
183	mouse_limit = MOUSE_LIMIT;
184	break;
185    case SET_EXT_MODE_MOUSE:
186	mouse_limit = EXT_MOUSE_LIMIT;
187	break;
188    case SET_SGR_EXT_MODE_MOUSE:
189    case SET_URXVT_EXT_MODE_MOUSE:
190	mouse_limit = -1;
191	break;
192    }
193    return mouse_limit;
194}
195
196static unsigned
197EmitMousePosition(TScreen *screen, Char line[], unsigned count, int value)
198{
199    int mouse_limit = MouseLimit(screen);
200
201    /*
202     * Add pointer position to key sequence
203     *
204     * In extended mode we encode large positions as two-byte UTF-8.
205     *
206     * NOTE: historically, it was possible to emit 256, which became
207     * zero by truncation to 8 bits. While this was arguably a bug,
208     * it's also somewhat useful as a past-end marker. We preserve
209     * this behavior for both normal and extended mouse modes.
210     */
211    switch (screen->extend_coords) {
212    default:
213	if (value == mouse_limit) {
214	    line[count++] = CharOf(0);
215	} else {
216	    line[count++] = CharOf(' ' + value + 1);
217	}
218	break;
219    case SET_EXT_MODE_MOUSE:
220	if (value == mouse_limit) {
221	    line[count++] = CharOf(0);
222	} else if (value < EXT_MOUSE_START) {
223	    line[count++] = CharOf(' ' + value + 1);
224	} else {
225	    value += ' ' + 1;
226	    line[count++] = CharOf(0xC0 + (value >> 6));
227	    line[count++] = CharOf(0x80 + (value & 0x3F));
228	}
229	break;
230    case SET_SGR_EXT_MODE_MOUSE:
231	/* FALLTHRU */
232    case SET_URXVT_EXT_MODE_MOUSE:
233	count += (unsigned) sprintf((char *) line + count, "%d", value + 1);
234	break;
235    }
236    return count;
237}
238
239static unsigned
240EmitMousePositionSeparator(TScreen *screen, Char line[], unsigned count)
241{
242    switch (screen->extend_coords) {
243    case SET_SGR_EXT_MODE_MOUSE:
244    case SET_URXVT_EXT_MODE_MOUSE:
245	line[count++] = ';';
246	break;
247    }
248    return count;
249}
250
251Bool
252SendMousePosition(XtermWidget xw, XEvent *event)
253{
254    XButtonEvent *my_event = (XButtonEvent *) event;
255    Bool result = False;
256
257    switch (okSendMousePos(xw)) {
258    case MOUSE_OFF:
259	/* If send_mouse_pos mode isn't on, we shouldn't be here */
260	break;
261
262    case BTN_EVENT_MOUSE:
263    case ANY_EVENT_MOUSE:
264	if (KeyModifiers(event) == 0 || KeyModifiers(event) == ControlMask) {
265	    /* xterm extension for motion reporting. June 1998 */
266	    /* EditorButton() will distinguish between the modes */
267	    switch (event->type) {
268	    case MotionNotify:
269		my_event->button = 0;
270		/* FALLTHRU */
271	    case ButtonPress:
272		/* FALLTHRU */
273	    case ButtonRelease:
274		EditorButton(xw, my_event);
275		result = True;
276		break;
277	    }
278	}
279	break;
280
281    case X10_MOUSE:		/* X10 compatibility sequences */
282	if (IsBtnEvent(event)) {
283	    if (BtnModifiers(my_event) == 0) {
284		if (my_event->type == ButtonPress)
285		    EditorButton(xw, my_event);
286		result = True;
287	    }
288	}
289	break;
290
291    case VT200_HIGHLIGHT_MOUSE:	/* DEC vt200 hilite tracking */
292	if (IsBtnEvent(event)) {
293	    if (my_event->type == ButtonPress &&
294		BtnModifiers(my_event) == 0 &&
295		my_event->button == Button1) {
296		TrackDown(xw, my_event);
297		result = True;
298	    } else if (BtnModifiers(my_event) == 0
299		       || BtnModifiers(my_event) == ControlMask) {
300		EditorButton(xw, my_event);
301		result = True;
302	    }
303	}
304	break;
305
306    case VT200_MOUSE:		/* DEC vt200 compatible */
307	if (IsBtnEvent(event)) {
308	    if (BtnModifiers(my_event) == 0
309		|| BtnModifiers(my_event) == ControlMask) {
310		EditorButton(xw, my_event);
311		result = True;
312	    }
313	}
314	break;
315
316    case DEC_LOCATOR:
317	if (IsBtnEvent(event)) {
318#if OPT_DEC_LOCATOR
319	    result = SendLocatorPosition(xw, my_event);
320#endif /* OPT_DEC_LOCATOR */
321	}
322	break;
323    }
324    return result;
325}
326
327#if OPT_DEC_LOCATOR
328
329#define	LocatorCoords( row, col, x, y, oor )			\
330    if( screen->locator_pixels ) {				\
331	(oor)=False; (row) = (y)+1; (col) = (x)+1;		\
332	/* Limit to screen dimensions */			\
333	if ((row) < 1) (row) = 1,(oor)=True;			\
334	else if ((row) > screen->border*2+Height(screen))	\
335	    (row) = screen->border*2+Height(screen),(oor)=True;	\
336	if ((col) < 1) (col) = 1,(oor)=True;			\
337	else if ((col) > OriginX(screen)*2+Width(screen))	\
338	    (col) = OriginX(screen)*2+Width(screen),(oor)=True;	\
339    } else {							\
340	(oor)=False;						\
341	/* Compute character position of mouse pointer */	\
342	(row) = ((y) - screen->border) / FontHeight(screen);	\
343	(col) = ((x) - OriginX(screen)) / FontWidth(screen);	\
344	/* Limit to screen dimensions */			\
345	if ((row) < 0) (row) = 0,(oor)=True;			\
346	else if ((row) > screen->max_row)			\
347	    (row) = screen->max_row,(oor)=True;			\
348	if ((col) < 0) (col) = 0,(oor)=True;			\
349	else if ((col) > screen->max_col)			\
350	    (col) = screen->max_col,(oor)=True;			\
351	(row)++; (col)++;					\
352    }
353
354static Bool
355SendLocatorPosition(XtermWidget xw, XButtonEvent *event)
356{
357    ANSI reply;
358    TScreen *screen = TScreenOf(xw);
359    int row, col;
360    Bool oor;
361    int button;
362    unsigned state;
363
364    /* Make sure the event is an appropriate type */
365    if ((!IsBtnEvent(event) &&
366	 !screen->loc_filter) ||
367	(BtnModifiers(event) != 0 && BtnModifiers(event) != ControlMask))
368	return (False);
369
370    if ((event->type == ButtonPress &&
371	 !(screen->locator_events & LOC_BTNS_DN)) ||
372	(event->type == ButtonRelease &&
373	 !(screen->locator_events & LOC_BTNS_UP)))
374	return (True);
375
376    if (event->type == MotionNotify) {
377	CheckLocatorPosition(xw, event);
378	return (True);
379    }
380
381    /* get button # */
382    button = (int) event->button - 1;
383
384    LocatorCoords(row, col, event->x, event->y, oor);
385
386    /*
387     * DECterm mouse:
388     *
389     * ESCAPE '[' event ; mask ; row ; column '&' 'w'
390     */
391    memset(&reply, 0, sizeof(reply));
392    reply.a_type = ANSI_CSI;
393
394    if (oor) {
395	reply.a_nparam = 1;
396	reply.a_param[0] = 0;	/* Event - 0 = locator unavailable */
397	reply.a_inters = '&';
398	reply.a_final = 'w';
399	unparseseq(xw, &reply);
400
401	if (screen->locator_reset) {
402	    MotionOff(screen, xw);
403	    screen->send_mouse_pos = MOUSE_OFF;
404	}
405	return (True);
406    }
407
408    /*
409     * event:
410     *        1       no buttons
411     *        2       left button down
412     *        3       left button up
413     *        4       middle button down
414     *        5       middle button up
415     *        6       right button down
416     *        7       right button up
417     *        8       M4 down
418     *        9       M4 up
419     */
420    reply.a_nparam = 4;
421    switch (event->type) {
422    case ButtonPress:
423	reply.a_param[0] = (ParmType) (2 + (button << 1));
424	break;
425    case ButtonRelease:
426	reply.a_param[0] = (ParmType) (3 + (button << 1));
427	break;
428    default:
429	return (True);
430    }
431    /*
432     * mask:
433     * bit7   bit6   bit5   bit4   bit3     bit2       bit1         bit0
434     *                             M4 down  left down  middle down  right down
435     *
436     * Notice that Button1 (left) and Button3 (right) are swapped in the mask.
437     * Also, mask should be the state after the button press/release,
438     * X provides the state not including the button press/release.
439     */
440    state = (event->state
441	     & (Button1Mask | Button2Mask | Button3Mask | Button4Mask)) >> 8;
442    /* update mask to "after" state */
443    state ^= ((unsigned) (1 << button));
444    /* swap Button1 & Button3 */
445    state = ((state & (unsigned) ~(4 | 1))
446	     | ((state & 1) ? 4 : 0)
447	     | ((state & 4) ? 1 : 0));
448
449    reply.a_param[1] = (ParmType) state;
450    reply.a_param[2] = (ParmType) row;
451    reply.a_param[3] = (ParmType) col;
452    reply.a_inters = '&';
453    reply.a_final = 'w';
454
455    unparseseq(xw, &reply);
456
457    if (screen->locator_reset) {
458	MotionOff(screen, xw);
459	screen->send_mouse_pos = MOUSE_OFF;
460    }
461
462    /*
463     * DECterm turns the Locator off if a button is pressed while a filter
464     * rectangle is active.  This might be a bug, but I don't know, so I'll
465     * emulate it anyway.
466     */
467    if (screen->loc_filter) {
468	screen->send_mouse_pos = MOUSE_OFF;
469	screen->loc_filter = False;
470	screen->locator_events = 0;
471	MotionOff(screen, xw);
472    }
473
474    return (True);
475}
476
477/*
478 * mask:
479 * bit 7   bit 6   bit 5   bit 4   bit 3   bit 2       bit 1         bit 0
480 *                                 M4 down left down   middle down   right down
481 *
482 * Button1 (left) and Button3 (right) are swapped in the mask relative to X.
483 */
484#define	ButtonState(state, mask)	\
485{ int stemp = (int) (((mask) & (Button1Mask | Button2Mask | Button3Mask | Button4Mask)) >> 8);	\
486  /* swap Button1 & Button3 */								\
487  (state) = (stemp & ~(4|1)) | ((stemp & 1) ? 4 : 0) | ((stemp & 4) ? 1 : 0);			\
488}
489
490void
491GetLocatorPosition(XtermWidget xw)
492{
493    ANSI reply;
494    TScreen *screen = TScreenOf(xw);
495    Window root, child;
496    int rx, ry, x, y;
497    unsigned int mask;
498    int row = 0, col = 0;
499    Bool oor = False;
500    Bool ret = False;
501    int state;
502
503    /*
504     * DECterm turns the Locator off if the position is requested while a
505     * filter rectangle is active.  This might be a bug, but I don't know, so
506     * I'll emulate it anyways.
507     */
508    if (screen->loc_filter) {
509	screen->send_mouse_pos = MOUSE_OFF;
510	screen->loc_filter = False;
511	screen->locator_events = 0;
512	MotionOff(screen, xw);
513    }
514
515    memset(&reply, 0, sizeof(reply));
516    reply.a_type = ANSI_CSI;
517
518    if (okSendMousePos(xw) == DEC_LOCATOR) {
519	ret = XQueryPointer(screen->display, VWindow(screen), &root,
520			    &child, &rx, &ry, &x, &y, &mask);
521	if (ret) {
522	    LocatorCoords(row, col, x, y, oor);
523	}
524    }
525    if (ret == False || oor) {
526	reply.a_nparam = 1;
527	reply.a_param[0] = 0;	/* Event - 0 = locator unavailable */
528	reply.a_inters = '&';
529	reply.a_final = 'w';
530	unparseseq(xw, &reply);
531
532	if (screen->locator_reset) {
533	    MotionOff(screen, xw);
534	    screen->send_mouse_pos = MOUSE_OFF;
535	}
536	return;
537    }
538
539    ButtonState(state, mask);
540
541    reply.a_nparam = 4;
542    reply.a_param[0] = 1;	/* Event - 1 = response to locator request */
543    reply.a_param[1] = (ParmType) state;
544    reply.a_param[2] = (ParmType) row;
545    reply.a_param[3] = (ParmType) col;
546    reply.a_inters = '&';
547    reply.a_final = 'w';
548    unparseseq(xw, &reply);
549
550    if (screen->locator_reset) {
551	MotionOff(screen, xw);
552	screen->send_mouse_pos = MOUSE_OFF;
553    }
554}
555
556void
557InitLocatorFilter(XtermWidget xw)
558{
559    ANSI reply;
560    TScreen *screen = TScreenOf(xw);
561    Window root, child;
562    int rx, ry, x, y;
563    unsigned int mask;
564    int row = 0, col = 0;
565    Bool oor = 0;
566    Bool ret;
567
568    ret = XQueryPointer(screen->display, VWindow(screen),
569			&root, &child, &rx, &ry, &x, &y, &mask);
570    if (ret) {
571	LocatorCoords(row, col, x, y, oor);
572    }
573    if (ret == False || oor) {
574	/* Locator is unavailable */
575
576	if (screen->loc_filter_top != LOC_FILTER_POS ||
577	    screen->loc_filter_left != LOC_FILTER_POS ||
578	    screen->loc_filter_bottom != LOC_FILTER_POS ||
579	    screen->loc_filter_right != LOC_FILTER_POS) {
580	    /*
581	     * If any explicit coordinates were received,
582	     * report immediately with no coordinates.
583	     */
584	    memset(&reply, 0, sizeof(reply));
585	    reply.a_type = ANSI_CSI;
586	    reply.a_nparam = 1;
587	    reply.a_param[0] = 0;	/* Event - 0 = locator unavailable */
588	    reply.a_inters = '&';
589	    reply.a_final = 'w';
590	    unparseseq(xw, &reply);
591
592	    if (screen->locator_reset) {
593		MotionOff(screen, xw);
594		screen->send_mouse_pos = MOUSE_OFF;
595	    }
596	} else {
597	    /*
598	     * No explicit coordinates were received, and the pointer is
599	     * unavailable.  Report when the pointer re-enters the window.
600	     */
601	    screen->loc_filter = True;
602	    MotionOn(screen, xw);
603	}
604	return;
605    }
606
607    /*
608     * Adjust rectangle coordinates:
609     *  1. Replace "LOC_FILTER_POS" with current coordinates
610     *  2. Limit coordinates to screen size
611     *  3. make sure top and left are less than bottom and right, resp.
612     */
613    if (screen->locator_pixels) {
614	rx = OriginX(screen) * 2 + Width(screen);
615	ry = screen->border * 2 + Height(screen);
616    } else {
617	rx = screen->max_col;
618	ry = screen->max_row;
619    }
620
621#define	Adjust( coord, def, max )				\
622	if( (coord) == LOC_FILTER_POS )	(coord) = (def);	\
623	else if ((coord) < 1)		(coord) = 1;		\
624	else if ((coord) > (max))	(coord) = (max)
625
626    Adjust(screen->loc_filter_top, row, ry);
627    Adjust(screen->loc_filter_left, col, rx);
628    Adjust(screen->loc_filter_bottom, row, ry);
629    Adjust(screen->loc_filter_right, col, rx);
630
631    if (screen->loc_filter_top > screen->loc_filter_bottom) {
632	ry = screen->loc_filter_top;
633	screen->loc_filter_top = screen->loc_filter_bottom;
634	screen->loc_filter_bottom = ry;
635    }
636
637    if (screen->loc_filter_left > screen->loc_filter_right) {
638	rx = screen->loc_filter_left;
639	screen->loc_filter_left = screen->loc_filter_right;
640	screen->loc_filter_right = rx;
641    }
642
643    if ((col < screen->loc_filter_left) ||
644	(col > screen->loc_filter_right) ||
645	(row < screen->loc_filter_top) ||
646	(row > screen->loc_filter_bottom)) {
647	int state;
648
649	/* Pointer is already outside the rectangle - report immediately */
650	ButtonState(state, mask);
651
652	memset(&reply, 0, sizeof(reply));
653	reply.a_type = ANSI_CSI;
654	reply.a_nparam = 4;
655	reply.a_param[0] = 10;	/* Event - 10 = locator outside filter */
656	reply.a_param[1] = (ParmType) state;
657	reply.a_param[2] = (ParmType) row;
658	reply.a_param[3] = (ParmType) col;
659	reply.a_inters = '&';
660	reply.a_final = 'w';
661	unparseseq(xw, &reply);
662
663	if (screen->locator_reset) {
664	    MotionOff(screen, xw);
665	    screen->send_mouse_pos = MOUSE_OFF;
666	}
667	return;
668    }
669
670    /*
671     * Rectangle is set up.  Allow pointer tracking
672     * to detect if the mouse leaves the rectangle.
673     */
674    screen->loc_filter = True;
675    MotionOn(screen, xw);
676}
677
678static void
679CheckLocatorPosition(XtermWidget xw, XButtonEvent *event)
680{
681    ANSI reply;
682    TScreen *screen = TScreenOf(xw);
683    int row, col;
684    Bool oor;
685
686    LocatorCoords(row, col, event->x, event->y, oor);
687
688    /*
689     * Send report if the pointer left the filter rectangle, if
690     * the pointer left the window, or if the filter rectangle
691     * had no coordinates and the pointer re-entered the window.
692     */
693    if (oor || (screen->loc_filter_top == LOC_FILTER_POS) ||
694	(col < screen->loc_filter_left) ||
695	(col > screen->loc_filter_right) ||
696	(row < screen->loc_filter_top) ||
697	(row > screen->loc_filter_bottom)) {
698	/* Filter triggered - disable it */
699	screen->loc_filter = False;
700	MotionOff(screen, xw);
701
702	memset(&reply, 0, sizeof(reply));
703	reply.a_type = ANSI_CSI;
704	if (oor) {
705	    reply.a_nparam = 1;
706	    reply.a_param[0] = 0;	/* Event - 0 = locator unavailable */
707	} else {
708	    int state;
709
710	    ButtonState(state, event->state);
711
712	    reply.a_nparam = 4;
713	    reply.a_param[0] = 10;	/* Event - 10 = locator outside filter */
714	    reply.a_param[1] = (ParmType) state;
715	    reply.a_param[2] = (ParmType) row;
716	    reply.a_param[3] = (ParmType) col;
717	}
718
719	reply.a_inters = '&';
720	reply.a_final = 'w';
721	unparseseq(xw, &reply);
722
723	if (screen->locator_reset) {
724	    MotionOff(screen, xw);
725	    screen->send_mouse_pos = MOUSE_OFF;
726	}
727    }
728}
729#endif /* OPT_DEC_LOCATOR */
730
731#if OPT_READLINE
732static int
733isClick1_clean(XtermWidget xw, XButtonEvent *event)
734{
735    TScreen *screen = TScreenOf(xw);
736    int delta;
737
738    if (!IsBtnEvent(event)
739    /* Disable on Shift-Click-1, including the application-mouse modes */
740	|| (BtnModifiers(event) & ShiftMask)
741	|| (okSendMousePos(xw) != MOUSE_OFF)	/* Kinda duplicate... */
742	||ExtendingSelection)	/* Was moved */
743	return 0;
744
745    if (event->type != ButtonRelease)
746	return 0;
747
748    if (lastButtonDownTime == (Time) 0) {
749	/* first time or once in a blue moon */
750	delta = screen->multiClickTime + 1;
751    } else if (event->time > lastButtonDownTime) {
752	/* most of the time */
753	delta = (int) (event->time - lastButtonDownTime);
754    } else {
755	/* time has rolled over since lastButtonUpTime */
756	delta = (int) ((((Time) ~ 0) - lastButtonDownTime) + event->time);
757    }
758
759    return delta <= screen->multiClickTime;
760}
761
762static int
763isDoubleClick3(TScreen *screen, XButtonEvent *event)
764{
765    int delta;
766
767    if (event->type != ButtonRelease
768	|| (BtnModifiers(event) & ShiftMask)
769	|| event->button != Button3) {
770	lastButton3UpTime = 0;	/* Disable the cached info */
771	return 0;
772    }
773    /* Process Btn3Release. */
774    if (lastButton3DoubleDownTime == (Time) 0) {
775	/* No previous click or once in a blue moon */
776	delta = screen->multiClickTime + 1;
777    } else if (event->time > lastButton3DoubleDownTime) {
778	/* most of the time */
779	delta = (int) (event->time - lastButton3DoubleDownTime);
780    } else {
781	/* time has rolled over since lastButton3DoubleDownTime */
782	delta = (int) ((((Time) ~ 0) - lastButton3DoubleDownTime) + event->time);
783    }
784    if (delta <= screen->multiClickTime) {
785	/* Double click */
786	CELL cell;
787
788	/* Cannot check ExtendingSelection, since mouse-3 always sets it */
789	PointToCELL(screen, event->y, event->x, &cell);
790	if (isSameCELL(&cell, &lastButton3)) {
791	    lastButton3DoubleDownTime = 0;	/* Disable the third click */
792	    return 1;
793	}
794    }
795    /* Not a double click, memorize for future check. */
796    lastButton3UpTime = event->time;
797    PointToCELL(screen, event->y, event->x, &lastButton3);
798    return 0;
799}
800
801static int
802CheckSecondPress3(TScreen *screen, XEvent *event)
803{
804    int delta;
805
806    if (event->type != ButtonPress
807	|| (KeyModifiers(event) & ShiftMask)
808	|| event->xbutton.button != Button3) {
809	lastButton3DoubleDownTime = 0;	/* Disable the cached info */
810	return 0;
811    }
812    /* Process Btn3Press. */
813    if (lastButton3UpTime == (Time) 0) {
814	/* No previous click or once in a blue moon */
815	delta = screen->multiClickTime + 1;
816    } else if (event->xbutton.time > lastButton3UpTime) {
817	/* most of the time */
818	delta = (int) (event->xbutton.time - lastButton3UpTime);
819    } else {
820	/* time has rolled over since lastButton3UpTime */
821	delta = (int) ((((Time) ~ 0) - lastButton3UpTime) + event->xbutton.time);
822    }
823    if (delta <= screen->multiClickTime) {
824	CELL cell;
825
826	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
827	if (isSameCELL(&cell, &lastButton3)) {
828	    /* A candidate for a double-click */
829	    lastButton3DoubleDownTime = event->xbutton.time;
830	    PointToCELL(screen, event->xbutton.y, event->xbutton.x, &lastButton3);
831	    return 1;
832	}
833	lastButton3UpTime = 0;	/* Disable the info about the previous click */
834    }
835    /* Either too long, or moved, disable. */
836    lastButton3DoubleDownTime = 0;
837    return 0;
838}
839
840static int
841rowOnCurrentLine(TScreen *screen,
842		 int line,
843		 int *deltap)	/* must be XButtonEvent */
844{
845    int result = 1;
846
847    *deltap = 0;
848
849    if (line != screen->cur_row) {
850	int l1, l2;
851
852	if (line < screen->cur_row)
853	    l1 = line, l2 = screen->cur_row;
854	else
855	    l2 = line, l1 = screen->cur_row;
856	l1--;
857	while (++l1 < l2) {
858	    LineData *ld = GET_LINEDATA(screen, l1);
859	    if (!LineTstWrapped(ld)) {
860		result = 0;
861		break;
862	    }
863	}
864	if (result) {
865	    /* Everything is on one "wrapped line" now */
866	    *deltap = line - screen->cur_row;
867	}
868    }
869    return result;
870}
871
872static int
873eventRow(TScreen *screen, XEvent *event)	/* must be XButtonEvent */
874{
875    return (event->xbutton.y - screen->border) / FontHeight(screen);
876}
877
878static int
879eventColBetween(TScreen *screen, XEvent *event)		/* must be XButtonEvent */
880{
881    /* Correct by half a width - we are acting on a boundary, not on a cell. */
882    return ((event->xbutton.x - OriginX(screen) + (FontWidth(screen) - 1) / 2)
883	    / FontWidth(screen));
884}
885
886static int
887ReadLineMovePoint(TScreen *screen, int col, int ldelta)
888{
889    Char line[6];
890    unsigned count = 0;
891
892    col += ldelta * MaxCols(screen) - screen->cur_col;
893    if (col == 0)
894	return 0;
895    if (screen->control_eight_bits) {
896	line[count++] = ANSI_CSI;
897    } else {
898	line[count++] = ANSI_ESC;
899	line[count++] = '[';	/* XXX maybe sometimes O is better? */
900    }
901    line[count] = CharOf(col > 0 ? 'C' : 'D');
902    if (col < 0)
903	col = -col;
904    while (col--)
905	v_write(screen->respond, line, 3);
906    return 1;
907}
908
909static int
910ReadLineDelete(TScreen *screen, CELL *cell1, CELL *cell2)
911{
912    int del;
913
914    del = (cell2->col - cell1->col) + ((cell2->row - cell1->row) * MaxCols(screen));
915    if (del <= 0)		/* Just in case... */
916	return 0;
917    while (del--)
918	v_write(screen->respond, (const Char *) "\177", 1);
919    return 1;
920}
921
922static void
923readlineExtend(XtermWidget xw, XEvent *event)
924{
925    TScreen *screen = TScreenOf(xw);
926    int ldelta1, ldelta2;
927
928    if (IsBtnEvent(event)) {
929	XButtonEvent *my_event = (XButtonEvent *) event;
930	if (isClick1_clean(xw, my_event)
931	    && SCREEN_FLAG(screen, click1_moves)
932	    && rowOnCurrentLine(screen, eventRow(screen, event), &ldelta1)) {
933	    ReadLineMovePoint(screen, eventColBetween(screen, event), ldelta1);
934	}
935	if (isDoubleClick3(screen, my_event)
936	    && SCREEN_FLAG(screen, dclick3_deletes)
937	    && rowOnCurrentLine(screen, screen->startSel.row, &ldelta1)
938	    && rowOnCurrentLine(screen, screen->endSel.row, &ldelta2)) {
939	    ReadLineMovePoint(screen, screen->endSel.col, ldelta2);
940	    ReadLineDelete(screen, &screen->startSel, &(screen->endSel));
941	}
942    }
943}
944
945#endif /* OPT_READLINE */
946
947/* ^XM-G<line+' '><col+' '> */
948void
949DiredButton(Widget w,
950	    XEvent *event,	/* must be XButtonEvent */
951	    String *params GCC_UNUSED,	/* selections */
952	    Cardinal *num_params GCC_UNUSED)
953{
954    XtermWidget xw;
955
956    if ((xw = getXtermWidget(w)) != 0) {
957	TScreen *screen = TScreenOf(xw);
958
959	if (IsBtnEvent(event)
960	    && (event->xbutton.y >= screen->border)
961	    && (event->xbutton.x >= OriginX(screen))) {
962	    Char Line[6];
963	    unsigned line, col;
964
965	    line = (unsigned) ((event->xbutton.y - screen->border)
966			       / FontHeight(screen));
967	    col = (unsigned) ((event->xbutton.x - OriginX(screen))
968			      / FontWidth(screen));
969	    Line[0] = CONTROL('X');
970	    Line[1] = ANSI_ESC;
971	    Line[2] = 'G';
972	    Line[3] = CharOf(' ' + col);
973	    Line[4] = CharOf(' ' + line);
974	    v_write(screen->respond, Line, 5);
975	}
976    }
977}
978
979#if OPT_READLINE
980void
981ReadLineButton(Widget w,
982	       XEvent *event,	/* must be XButtonEvent */
983	       String *params GCC_UNUSED,	/* selections */
984	       Cardinal *num_params GCC_UNUSED)
985{
986    XtermWidget xw;
987
988    if ((xw = getXtermWidget(w)) != 0) {
989	TScreen *screen = TScreenOf(xw);
990	Char Line[6];
991	int line, col, ldelta = 0;
992
993	if (!IsBtnEvent(event)
994	    || (okSendMousePos(xw) != MOUSE_OFF) || ExtendingSelection)
995	    goto finish;
996	if (event->type == ButtonRelease) {
997	    int delta;
998
999	    if (lastButtonDownTime == (Time) 0) {
1000		/* first time and once in a blue moon */
1001		delta = screen->multiClickTime + 1;
1002	    } else if (event->xbutton.time > lastButtonDownTime) {
1003		/* most of the time */
1004		delta = (int) (event->xbutton.time - lastButtonDownTime);
1005	    } else {
1006		/* time has rolled over since lastButtonUpTime */
1007		delta = (int) ((((Time) ~ 0) - lastButtonDownTime) + event->xbutton.time);
1008	    }
1009	    if (delta > screen->multiClickTime)
1010		goto finish;	/* All this work for this... */
1011	}
1012	line = (event->xbutton.y - screen->border) / FontHeight(screen);
1013	if (!rowOnCurrentLine(screen, line, &ldelta))
1014	    goto finish;
1015	/* Correct by half a width - we are acting on a boundary, not on a cell. */
1016	col = (event->xbutton.x - OriginX(screen) + (FontWidth(screen) - 1)
1017	       / 2)
1018	    / FontWidth(screen) - screen->cur_col + ldelta * MaxCols(screen);
1019	if (col == 0)
1020	    goto finish;
1021	Line[0] = ANSI_ESC;
1022	/* XXX: sometimes it is better to send '['? */
1023	Line[1] = 'O';
1024	Line[2] = CharOf(col > 0 ? 'C' : 'D');
1025	if (col < 0)
1026	    col = -col;
1027	while (col--)
1028	    v_write(screen->respond, Line, 3);
1029      finish:
1030	if (event->type == ButtonRelease)
1031	    do_select_end(xw, event, params, num_params, False);
1032    }
1033}
1034#endif /* OPT_READLINE */
1035
1036/* repeats <ESC>n or <ESC>p */
1037void
1038ViButton(Widget w,
1039	 XEvent *event,		/* must be XButtonEvent */
1040	 String *params GCC_UNUSED,	/* selections */
1041	 Cardinal *num_params GCC_UNUSED)
1042{
1043    XtermWidget xw;
1044
1045    if ((xw = getXtermWidget(w)) != 0) {
1046	TScreen *screen = TScreenOf(xw);
1047	int pty = screen->respond;
1048
1049	if (IsBtnEvent(event)) {
1050	    int line;
1051
1052	    line = screen->cur_row -
1053		((event->xbutton.y - screen->border) / FontHeight(screen));
1054
1055	    if (line != 0) {
1056		Char Line[6];
1057
1058		Line[0] = ANSI_ESC;	/* force an exit from insert-mode */
1059		v_write(pty, Line, 1);
1060
1061		if (line < 0) {
1062		    line = -line;
1063		    Line[0] = CONTROL('n');
1064		} else {
1065		    Line[0] = CONTROL('p');
1066		}
1067		while (--line >= 0)
1068		    v_write(pty, Line, 1);
1069	    }
1070	}
1071    }
1072}
1073
1074/*
1075 * This function handles button-motion events
1076 */
1077/*ARGSUSED*/
1078void
1079HandleSelectExtend(Widget w,
1080		   XEvent *event,	/* must be XMotionEvent */
1081		   String *params GCC_UNUSED,
1082		   Cardinal *num_params GCC_UNUSED)
1083{
1084    XtermWidget xw;
1085
1086    if ((xw = getXtermWidget(w)) != 0) {
1087	TScreen *screen = TScreenOf(xw);
1088	CELL cell;
1089
1090	TRACE(("HandleSelectExtend @%ld\n", event->xmotion.time));
1091
1092	screen->selection_time = event->xmotion.time;
1093	switch (screen->eventMode) {
1094	    /* If not in one of the DEC mouse-reporting modes */
1095	case LEFTEXTENSION:
1096	case RIGHTEXTENSION:
1097	    PointToCELL(screen, event->xmotion.y, event->xmotion.x, &cell);
1098	    ExtendExtend(xw, &cell);
1099	    break;
1100
1101	    /* If in motion reporting mode, send mouse position to
1102	       character process as a key sequence \E[M... */
1103	case NORMAL:
1104	    /* will get here if send_mouse_pos != MOUSE_OFF */
1105	    if (okSendMousePos(xw) == BTN_EVENT_MOUSE
1106		|| okSendMousePos(xw) == ANY_EVENT_MOUSE) {
1107		(void) SendMousePosition(xw, event);
1108	    }
1109	    break;
1110	}
1111    }
1112}
1113
1114void
1115HandleKeyboardSelectExtend(Widget w,
1116			   XEvent *event GCC_UNUSED,	/* must be XButtonEvent */
1117			   String *params GCC_UNUSED,
1118			   Cardinal *num_params GCC_UNUSED)
1119{
1120    XtermWidget xw;
1121
1122    if ((xw = getXtermWidget(w)) != 0) {
1123	TScreen *screen = TScreenOf(xw);
1124
1125	TRACE(("HandleKeyboardSelectExtend\n"));
1126	ExtendExtend(xw, &screen->cursorp);
1127    }
1128}
1129
1130static void
1131do_select_end(XtermWidget xw,
1132	      XEvent *event,	/* must be XButtonEvent */
1133	      String *params,	/* selections */
1134	      Cardinal *num_params,
1135	      Bool use_cursor_loc)
1136{
1137    TScreen *screen = TScreenOf(xw);
1138
1139    screen->selection_time = event->xbutton.time;
1140    TRACE(("do_select_end @%ld\n", screen->selection_time));
1141    switch (screen->eventMode) {
1142    case NORMAL:
1143	(void) SendMousePosition(xw, event);
1144	break;
1145    case LEFTEXTENSION:
1146    case RIGHTEXTENSION:
1147	EndExtend(xw, event, params, *num_params, use_cursor_loc);
1148#if OPT_READLINE
1149	readlineExtend(xw, event);
1150#endif /* OPT_READLINE */
1151	break;
1152    }
1153}
1154
1155void
1156HandleSelectEnd(Widget w,
1157		XEvent *event,	/* must be XButtonEvent */
1158		String *params,	/* selections */
1159		Cardinal *num_params)
1160{
1161    XtermWidget xw;
1162
1163    if ((xw = getXtermWidget(w)) != 0) {
1164	TRACE(("HandleSelectEnd\n"));
1165	do_select_end(xw, event, params, num_params, False);
1166    }
1167}
1168
1169void
1170HandleKeyboardSelectEnd(Widget w,
1171			XEvent *event,	/* must be XButtonEvent */
1172			String *params,		/* selections */
1173			Cardinal *num_params)
1174{
1175    XtermWidget xw;
1176
1177    if ((xw = getXtermWidget(w)) != 0) {
1178	TRACE(("HandleKeyboardSelectEnd\n"));
1179	do_select_end(xw, event, params, num_params, True);
1180    }
1181}
1182
1183/*
1184 * Copy the selection data to the given target(s).
1185 */
1186void
1187HandleCopySelection(Widget w,
1188		    XEvent *event,
1189		    String *params,	/* list of targets */
1190		    Cardinal *num_params)
1191{
1192    XtermWidget xw;
1193
1194    if ((xw = getXtermWidget(w)) != 0) {
1195	TRACE(("HandleCopySelection\n"));
1196	SelectSet(xw, event, params, *num_params);
1197    }
1198}
1199
1200struct _SelectionList {
1201    String *params;
1202    Cardinal count;
1203    Atom *targets;
1204    Time time;
1205};
1206
1207static unsigned
1208DECtoASCII(unsigned ch)
1209{
1210    if (xtermIsDecGraphic(ch)) {
1211	ch = CharOf("###########+++++##-##++++|######"[ch]);
1212	/*           01234567890123456789012345678901 */
1213    }
1214    return ch;
1215}
1216
1217#if OPT_WIDE_CHARS
1218static Cardinal
1219addXtermChar(Char **buffer, Cardinal *used, Cardinal offset, unsigned value)
1220{
1221    if (offset + 1 >= *used) {
1222	*used = 1 + (2 * (offset + 1));
1223	allocXtermChars(buffer, *used);
1224    }
1225    (*buffer)[offset++] = (Char) value;
1226    return offset;
1227}
1228#define AddChar(buffer, used, offset, value) \
1229	offset = addXtermChar(buffer, used, offset, (unsigned) value)
1230
1231/*
1232 * Convert a UTF-8 string to Latin-1, replacing non Latin-1 characters by `#',
1233 * or ASCII/Latin-1 equivalents for special cases.
1234 */
1235static Char *
1236UTF8toLatin1(TScreen *screen, Char *s, unsigned long len, unsigned long *result)
1237{
1238    static Char *buffer;
1239    static Cardinal used;
1240
1241    Cardinal offset = 0;
1242
1243    if (len != 0) {
1244	PtyData data;
1245
1246	fakePtyData(&data, s, s + len);
1247	while (decodeUtf8(screen, &data)) {
1248	    Bool fails = False;
1249	    Bool extra = False;
1250	    IChar value = skipPtyData(&data);
1251	    if (value == UCS_REPL) {
1252		fails = True;
1253	    } else if (value < 256) {
1254		AddChar(&buffer, &used, offset, CharOf(value));
1255	    } else {
1256		unsigned eqv = ucs2dec(value);
1257		if (xtermIsDecGraphic(eqv)) {
1258		    AddChar(&buffer, &used, offset, DECtoASCII(eqv));
1259		} else {
1260		    eqv = AsciiEquivs(value);
1261		    if (eqv == value) {
1262			fails = True;
1263		    } else {
1264			AddChar(&buffer, &used, offset, eqv);
1265		    }
1266		    if (isWide((wchar_t) value))
1267			extra = True;
1268		}
1269	    }
1270
1271	    /*
1272	     * If we're not able to plug in a single-byte result, insert the
1273	     * defaultString (which normally is a single "#", but could be
1274	     * whatever the user wants).
1275	     */
1276	    if (fails) {
1277		const Char *p;
1278
1279		for (p = (const Char *) screen->default_string; *p != '\0'; ++p) {
1280		    AddChar(&buffer, &used, offset, *p);
1281		}
1282	    }
1283	    if (extra)
1284		AddChar(&buffer, &used, offset, ' ');
1285	}
1286	AddChar(&buffer, &used, offset, '\0');
1287	*result = (unsigned long) (offset - 1);
1288    } else {
1289	*result = 0;
1290    }
1291    return buffer;
1292}
1293
1294int
1295xtermUtf8ToTextList(XtermWidget xw,
1296		    XTextProperty * text_prop,
1297		    char ***text_list,
1298		    int *text_list_count)
1299{
1300    TScreen *screen = TScreenOf(xw);
1301    Display *dpy = screen->display;
1302    int rc = -1;
1303
1304    if (text_prop->format == 8
1305	&& (rc = Xutf8TextPropertyToTextList(dpy, text_prop,
1306					     text_list,
1307					     text_list_count)) >= 0) {
1308	if (*text_list != NULL && *text_list_count != 0) {
1309	    int i;
1310	    Char *data;
1311	    char **new_text_list, *tmp;
1312	    unsigned long size, new_size;
1313
1314	    TRACE(("xtermUtf8ToTextList size %d\n", *text_list_count));
1315
1316	    /*
1317	     * XLib StringList actually uses only two pointers, one for the
1318	     * list itself, and one for the data.  Pointer to the data is the
1319	     * first element of the list, the rest (if any) list elements point
1320	     * to the same memory block as the first element
1321	     */
1322	    new_size = 0;
1323	    for (i = 0; i < *text_list_count; ++i) {
1324		data = (Char *) (*text_list)[i];
1325		size = strlen((*text_list)[i]) + 1;
1326		(void) UTF8toLatin1(screen, data, size, &size);
1327		new_size += size + 1;
1328	    }
1329	    new_text_list = TypeXtMallocN(char *, *text_list_count);
1330	    new_text_list[0] = tmp = XtMalloc((Cardinal) new_size);
1331	    for (i = 0; i < (*text_list_count); ++i) {
1332		data = (Char *) (*text_list)[i];
1333		size = strlen((*text_list)[i]) + 1;
1334		if ((data = UTF8toLatin1(screen, data, size, &size)) != 0) {
1335		    memcpy(tmp, data, size + 1);
1336		    new_text_list[i] = tmp;
1337		    tmp += size + 1;
1338		}
1339	    }
1340	    XFreeStringList((*text_list));
1341	    *text_list = new_text_list;
1342	} else {
1343	    rc = -1;
1344	}
1345    }
1346    return rc;
1347}
1348#endif /* OPT_WIDE_CHARS */
1349
1350static char *
1351parseItem(char *value, char *nextc)
1352{
1353    char *nextp = value;
1354    while (*nextp != '\0' && *nextp != ',') {
1355	*nextp = x_toupper(*nextp);
1356	++nextp;
1357    }
1358    *nextc = *nextp;
1359    *nextp = '\0';
1360
1361    return nextp;
1362}
1363
1364/*
1365 * All of the wanted strings are unique in the first character, so we can
1366 * use simple abbreviations.
1367 */
1368static Bool
1369sameItem(const char *actual, const char *wanted)
1370{
1371    Bool result = False;
1372    size_t have = strlen(actual);
1373    size_t need = strlen(wanted);
1374
1375    if (have != 0 && have <= need) {
1376	if (!strncmp(actual, wanted, have)) {
1377	    TRACE(("...matched \"%s\"\n", wanted));
1378	    result = True;
1379	}
1380    }
1381
1382    return result;
1383}
1384
1385/*
1386 * Handle the eightBitSelectTypes or utf8SelectTypes resource values.
1387 */
1388static Bool
1389overrideTargets(Widget w, String value, Atom **resultp)
1390{
1391    Bool override = False;
1392    XtermWidget xw;
1393
1394    if ((xw = getXtermWidget(w)) != 0) {
1395	TScreen *screen = TScreenOf(xw);
1396
1397	if (!IsEmpty(value)) {
1398	    char *copied = x_strdup(value);
1399	    if (copied != 0) {
1400		Atom *result = 0;
1401		Cardinal count = 1;
1402		int n;
1403
1404		TRACE(("decoding SelectTypes \"%s\"\n", value));
1405		for (n = 0; copied[n] != '\0'; ++n) {
1406		    if (copied[n] == ',')
1407			++count;
1408		}
1409		result = TypeXtMallocN(Atom, (2 * count) + 1);
1410		if (result == NULL) {
1411		    TRACE(("Couldn't allocate selection types\n"));
1412		} else {
1413		    char nextc = '?';
1414		    char *listp = (char *) copied;
1415		    count = 0;
1416		    do {
1417			char *nextp = parseItem(listp, &nextc);
1418			char *item = x_strtrim(listp);
1419			size_t len = (item ? strlen(item) : 0);
1420
1421			if (len == 0) {
1422			    /* EMPTY */ ;
1423			}
1424#if OPT_WIDE_CHARS
1425			else if (sameItem(item, "UTF8")) {
1426			    result[count++] = XA_UTF8_STRING(XtDisplay(w));
1427			}
1428#endif
1429			else if (sameItem(item, "I18N")) {
1430			    if (screen->i18nSelections) {
1431				result[count++] = XA_TEXT(XtDisplay(w));
1432				result[count++] = XA_COMPOUND_TEXT(XtDisplay(w));
1433			    }
1434			} else if (sameItem(item, "TEXT")) {
1435			    result[count++] = XA_TEXT(XtDisplay(w));
1436			} else if (sameItem(item, "COMPOUND_TEXT")) {
1437			    result[count++] = XA_COMPOUND_TEXT(XtDisplay(w));
1438			} else if (sameItem(item, "STRING")) {
1439			    result[count++] = XA_STRING;
1440			}
1441			*nextp++ = nextc;
1442			listp = nextp;
1443			free(item);
1444		    } while (nextc != '\0');
1445		    if (count) {
1446			result[count] = None;
1447			override = True;
1448			*resultp = result;
1449		    } else {
1450			XtFree((char *) result);
1451		    }
1452		}
1453		free(copied);
1454	    } else {
1455		TRACE(("Couldn't allocate copy of selection types\n"));
1456	    }
1457	}
1458    }
1459    return override;
1460}
1461
1462#if OPT_WIDE_CHARS
1463static Atom *
1464allocUtf8Targets(Widget w, TScreen *screen)
1465{
1466    Atom **resultp = &(screen->selection_targets_utf8);
1467
1468    if (*resultp == 0) {
1469	Atom *result;
1470
1471	if (!overrideTargets(w, screen->utf8_select_types, &result)) {
1472	    result = TypeXtMallocN(Atom, 5);
1473	    if (result == NULL) {
1474		TRACE(("Couldn't allocate utf-8 selection targets\n"));
1475	    } else {
1476		int n = 0;
1477
1478		if (XSupportsLocale()) {
1479		    result[n++] = XA_UTF8_STRING(XtDisplay(w));
1480#ifdef X_HAVE_UTF8_STRING
1481		    if (screen->i18nSelections) {
1482			result[n++] = XA_TEXT(XtDisplay(w));
1483			result[n++] = XA_COMPOUND_TEXT(XtDisplay(w));
1484		    }
1485#endif
1486		}
1487		result[n++] = XA_STRING;
1488		result[n] = None;
1489	    }
1490	}
1491
1492	*resultp = result;
1493    }
1494
1495    return *resultp;
1496}
1497#endif
1498
1499static Atom *
1500alloc8bitTargets(Widget w, TScreen *screen)
1501{
1502    Atom **resultp = &(screen->selection_targets_8bit);
1503
1504    if (*resultp == 0) {
1505	Atom *result = 0;
1506
1507	if (!overrideTargets(w, screen->eightbit_select_types, &result)) {
1508	    result = TypeXtMallocN(Atom, 5);
1509	    if (result == NULL) {
1510		TRACE(("Couldn't allocate 8bit selection targets\n"));
1511	    } else {
1512		int n = 0;
1513
1514		if (XSupportsLocale()) {
1515#ifdef X_HAVE_UTF8_STRING
1516		    result[n++] = XA_UTF8_STRING(XtDisplay(w));
1517#endif
1518		    if (screen->i18nSelections) {
1519			result[n++] = XA_TEXT(XtDisplay(w));
1520			result[n++] = XA_COMPOUND_TEXT(XtDisplay(w));
1521		    }
1522		}
1523		result[n++] = XA_STRING;
1524		result[n] = None;
1525	    }
1526	}
1527
1528	*resultp = result;
1529    }
1530
1531    return *resultp;
1532}
1533
1534static Atom *
1535_SelectionTargets(Widget w)
1536{
1537    Atom *result;
1538    XtermWidget xw;
1539
1540    if ((xw = getXtermWidget(w)) == 0) {
1541	result = NULL;
1542    } else {
1543	TScreen *screen = TScreenOf(xw);
1544
1545#if OPT_WIDE_CHARS
1546	if (screen->wide_chars) {
1547	    result = allocUtf8Targets(w, screen);
1548	} else
1549#endif
1550	{
1551	    /* not screen->wide_chars */
1552	    result = alloc8bitTargets(w, screen);
1553	}
1554    }
1555
1556    return result;
1557}
1558
1559#define isSELECT(value) (!strcmp(value, "SELECT"))
1560
1561static void
1562UnmapSelections(XtermWidget xw)
1563{
1564    TScreen *screen = TScreenOf(xw);
1565    Cardinal n;
1566
1567    if (screen->mappedSelect) {
1568	for (n = 0; screen->mappedSelect[n] != 0; ++n)
1569	    free((void *) screen->mappedSelect[n]);
1570	free(screen->mappedSelect);
1571	screen->mappedSelect = 0;
1572    }
1573}
1574
1575/*
1576 * xterm generally uses the primary selection.  Some applications prefer
1577 * (or are limited to) the clipboard.  Since the translations resource is
1578 * complicated, users seldom change the way it affects selection.  But it
1579 * is simple to remap the choice between primary and clipboard before the
1580 * call to XmuInternStrings().
1581 */
1582static String *
1583MapSelections(XtermWidget xw, String *params, Cardinal num_params)
1584{
1585    String *result = params;
1586
1587    if (params != 0 && num_params > 0) {
1588	Cardinal j;
1589	Boolean map = False;
1590
1591	for (j = 0; j < num_params; ++j) {
1592	    TRACE(("param[%d]:%s\n", j, params[j]));
1593	    if (isSELECT(params[j])) {
1594		map = True;
1595		break;
1596	    }
1597	}
1598	if (map) {
1599	    TScreen *screen = TScreenOf(xw);
1600	    const char *mapTo = (screen->selectToClipboard
1601				 ? "CLIPBOARD"
1602				 : "PRIMARY");
1603
1604	    UnmapSelections(xw);
1605	    if ((result = TypeMallocN(String, num_params + 1)) != 0) {
1606		result[num_params] = 0;
1607		for (j = 0; j < num_params; ++j) {
1608		    result[j] = x_strdup((isSELECT(params[j])
1609					  ? mapTo
1610					  : params[j]));
1611		    if (result[j] == 0) {
1612			UnmapSelections(xw);
1613			while (j != 0) {
1614			    free((void *) result[--j]);
1615			}
1616			free(result);
1617			result = 0;
1618			break;
1619		    }
1620		}
1621		screen->mappedSelect = result;
1622	    }
1623	}
1624    }
1625    return result;
1626}
1627
1628/*
1629 * Lookup the cut-buffer number, which will be in the range 0-7.
1630 * If it is not a cut-buffer, it is the primary selection (-1).
1631 */
1632static int
1633CutBuffer(Atom code)
1634{
1635    int cutbuffer;
1636    switch ((unsigned) code) {
1637    case XA_CUT_BUFFER0:
1638	cutbuffer = 0;
1639	break;
1640    case XA_CUT_BUFFER1:
1641	cutbuffer = 1;
1642	break;
1643    case XA_CUT_BUFFER2:
1644	cutbuffer = 2;
1645	break;
1646    case XA_CUT_BUFFER3:
1647	cutbuffer = 3;
1648	break;
1649    case XA_CUT_BUFFER4:
1650	cutbuffer = 4;
1651	break;
1652    case XA_CUT_BUFFER5:
1653	cutbuffer = 5;
1654	break;
1655    case XA_CUT_BUFFER6:
1656	cutbuffer = 6;
1657	break;
1658    case XA_CUT_BUFFER7:
1659	cutbuffer = 7;
1660	break;
1661    default:
1662	cutbuffer = -1;
1663	break;
1664    }
1665    TRACE(("CutBuffer(%d) = %d\n", (int) code, cutbuffer));
1666    return cutbuffer;
1667}
1668
1669#if OPT_PASTE64
1670static void
1671FinishPaste64(XtermWidget xw)
1672{
1673    TScreen *screen = TScreenOf(xw);
1674
1675    TRACE(("FinishPaste64(%d)\n", screen->base64_paste));
1676    if (screen->base64_paste) {
1677	screen->base64_paste = 0;
1678	unparseputc1(xw, screen->base64_final);
1679	unparse_end(xw);
1680    }
1681}
1682#endif
1683
1684#if !OPT_PASTE64
1685static
1686#endif
1687void
1688xtermGetSelection(Widget w,
1689		  Time ev_time,
1690		  String *params,	/* selections in precedence order */
1691		  Cardinal num_params,
1692		  Atom *targets)
1693{
1694    Atom selection;
1695    int cutbuffer;
1696    Atom target;
1697
1698    XtermWidget xw;
1699
1700    if (num_params == 0)
1701	return;
1702    if ((xw = getXtermWidget(w)) == 0)
1703	return;
1704
1705    TRACE(("xtermGetSelection num_params %d @%ld\n", num_params, ev_time));
1706    params = MapSelections(xw, params, num_params);
1707
1708    XmuInternStrings(XtDisplay(w), params, (Cardinal) 1, &selection);
1709    cutbuffer = CutBuffer(selection);
1710
1711    TRACE(("Cutbuffer: %d, target: %s\n", cutbuffer,
1712	   (targets
1713	    ? visibleSelectionTarget(XtDisplay(w), targets[0])
1714	    : "None")));
1715
1716    if (cutbuffer >= 0) {
1717	int inbytes;
1718	unsigned long nbytes;
1719	int fmt8 = 8;
1720	Atom type = XA_STRING;
1721	char *line;
1722
1723	/* 'line' is freed in SelectionReceived */
1724	line = XFetchBuffer(XtDisplay(w), &inbytes, cutbuffer);
1725	nbytes = (unsigned long) inbytes;
1726
1727	if (nbytes > 0) {
1728	    SelectionReceived(w, NULL, &selection, &type, (XtPointer) line,
1729			      &nbytes, &fmt8);
1730	} else if (num_params > 1) {
1731	    xtermGetSelection(w, ev_time, params + 1, num_params - 1, NULL);
1732	}
1733#if OPT_PASTE64
1734	else {
1735	    FinishPaste64(xw);
1736	}
1737#endif
1738    } else {
1739
1740	if (targets == NULL || targets[0] == None) {
1741	    targets = _SelectionTargets(w);
1742	}
1743
1744	if (targets != 0) {
1745	    struct _SelectionList *list;
1746
1747	    target = targets[0];
1748
1749	    if (targets[1] == None) {	/* last target in list */
1750		params++;
1751		num_params--;
1752		targets = _SelectionTargets(w);
1753	    } else {
1754		targets = &(targets[1]);
1755	    }
1756
1757	    if (num_params) {
1758		/* 'list' is freed in SelectionReceived */
1759		list = TypeXtMalloc(struct _SelectionList);
1760		if (list != 0) {
1761		    list->params = params;
1762		    list->count = num_params;
1763		    list->targets = targets;
1764		    list->time = ev_time;
1765		}
1766	    } else {
1767		list = NULL;
1768	    }
1769
1770	    XtGetSelectionValue(w, selection,
1771				target,
1772				SelectionReceived,
1773				(XtPointer) list, ev_time);
1774	}
1775    }
1776}
1777
1778#if OPT_TRACE && OPT_WIDE_CHARS
1779static void
1780GettingSelection(Display *dpy, Atom type, Char *line, unsigned long len)
1781{
1782    Char *cp;
1783    const char *name = TraceAtomName(dpy, type);
1784
1785    TRACE(("Getting %s (type=%ld, length=%ld)\n", name, (long int) type, len));
1786    for (cp = line; cp < line + len; cp++) {
1787	TRACE(("[%d:%lu]", (int) (cp + 1 - line), len));
1788	if (isprint(*cp)) {
1789	    TRACE(("%c\n", *cp));
1790	} else {
1791	    TRACE(("\\x%02x\n", *cp));
1792	}
1793    }
1794}
1795#else
1796#define GettingSelection(dpy,type,line,len)	/* nothing */
1797#endif
1798
1799#ifdef VMS
1800#  define tty_vwrite(pty,lag,l)		tt_write(lag,l)
1801#else /* !( VMS ) */
1802#  define tty_vwrite(pty,lag,l)		v_write(pty,lag,l)
1803#endif /* defined VMS */
1804
1805#if OPT_PASTE64
1806/* Return base64 code character given 6-bit number */
1807static const char base64_code[] = "\
1808ABCDEFGHIJKLMNOPQRSTUVWXYZ\
1809abcdefghijklmnopqrstuvwxyz\
18100123456789+/";
1811static void
1812base64_flush(TScreen *screen)
1813{
1814    Char x;
1815
1816    TRACE(("base64_flush count %d, pad %d (%d)\n",
1817	   screen->base64_count,
1818	   screen->base64_pad,
1819	   screen->base64_pad & 3));
1820
1821    switch (screen->base64_count) {
1822    case 0:
1823	break;
1824    case 2:
1825	x = CharOf(base64_code[screen->base64_accu << 4]);
1826	tty_vwrite(screen->respond, &x, 1);
1827	break;
1828    case 4:
1829	x = CharOf(base64_code[screen->base64_accu << 2]);
1830	tty_vwrite(screen->respond, &x, 1);
1831	break;
1832    }
1833    if (screen->base64_pad & 3) {
1834	tty_vwrite(screen->respond,
1835		   (const Char *) "===",
1836		   (unsigned) (3 - (screen->base64_pad & 3)));
1837    }
1838    screen->base64_count = 0;
1839    screen->base64_accu = 0;
1840    screen->base64_pad = 0;
1841}
1842#endif /* OPT_PASTE64 */
1843
1844/*
1845 * Translate ISO-8859-1 or UTF-8 data to NRCS.
1846 */
1847static void
1848ToNational(TScreen *screen, Char *buffer, unsigned *length)
1849{
1850    int gsetL = screen->gsets[screen->curgl];
1851    int gsetR = screen->gsets[screen->curgr];
1852
1853#if OPT_WIDE_CHARS
1854    if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
1855	Char *p;
1856	PtyData *data = TypeXtMallocX(PtyData, *length);
1857
1858	memset(data, 0, sizeof(*data));
1859	data->next = data->buffer;
1860	data->last = data->buffer + *length;
1861	memcpy(data->buffer, buffer, (size_t) *length);
1862	p = buffer;
1863	while (data->next < data->last) {
1864	    unsigned chr, out, gl, gr;
1865
1866	    if (!decodeUtf8(screen, data)) {
1867		data->utf_size = 1;
1868		data->utf_data = data->next[0];
1869	    }
1870	    data->next += data->utf_size;
1871	    chr = data->utf_data;
1872	    out = chr;
1873	    if ((gl = xtermCharSetIn(screen, chr, gsetL)) != chr) {
1874		out = gl;
1875	    } else if ((gr = xtermCharSetIn(screen, chr, gsetR)) != chr) {
1876		out = gr;
1877	    }
1878	    *p++ = (Char) ((out < 256) ? out : ' ');
1879	}
1880	*length = (unsigned) (p - buffer);
1881	free(data);
1882    } else
1883#endif
1884    {
1885	Char *p;
1886
1887	for (p = buffer; (int) (p - buffer) < (int) *length; ++p) {
1888	    unsigned gl, gr;
1889	    unsigned chr = *p;
1890	    unsigned out = chr;
1891	    if ((gl = xtermCharSetIn(screen, chr, gsetL)) != chr) {
1892		out = gl;
1893	    } else if ((gr = xtermCharSetIn(screen, chr, gsetR)) != chr) {
1894		out = gr;
1895	    }
1896	    *p = (Char) out;
1897	}
1898    }
1899}
1900
1901static void
1902_qWriteSelectionData(XtermWidget xw, Char *lag, unsigned length)
1903{
1904    TScreen *screen = TScreenOf(xw);
1905
1906    /*
1907     * If we are pasting into a window which is using NRCS, we want to map
1908     * the text from the normal encoding (ISO-8859-1 or UTF-8) into the coding
1909     * that an application would use to write characters with NRCS.
1910     *
1911     * TODO: handle conversion from UTF-8, and adjust length.  This can be done
1912     * in the same buffer because the target is always 8-bit.
1913     */
1914    if ((xw->flags & NATIONAL) && (length != 0)) {
1915	ToNational(screen, lag, &length);
1916    }
1917#if OPT_PASTE64
1918    if (screen->base64_paste) {
1919	/* Send data as base64 */
1920	Char *p = lag;
1921	Char buf[64];
1922	unsigned x = 0;
1923
1924	TRACE(("convert to base64 %d:%s\n", length, visibleChars(p, length)));
1925
1926	/*
1927	 * Handle the case where the selection is from _this_ xterm, which
1928	 * puts part of the reply in the buffer before the selection callback
1929	 * happens.
1930	 */
1931	if (screen->base64_paste && screen->unparse_len) {
1932	    unparse_end(xw);
1933	}
1934	while (length--) {
1935	    switch (screen->base64_count) {
1936	    case 0:
1937		buf[x++] = CharOf(base64_code[*p >> 2]);
1938		screen->base64_accu = (unsigned) (*p & 0x3);
1939		screen->base64_count = 2;
1940		++p;
1941		break;
1942	    case 2:
1943		buf[x++] = CharOf(base64_code[(screen->base64_accu << 4) +
1944					      (*p >> 4)]);
1945		screen->base64_accu = (unsigned) (*p & 0xF);
1946		screen->base64_count = 4;
1947		++p;
1948		break;
1949	    case 4:
1950		buf[x++] = CharOf(base64_code[(screen->base64_accu << 2) +
1951					      (*p >> 6)]);
1952		buf[x++] = CharOf(base64_code[*p & 0x3F]);
1953		screen->base64_accu = 0;
1954		screen->base64_count = 0;
1955		++p;
1956		break;
1957	    }
1958	    if (x >= 63) {
1959		/* Write 63 or 64 characters */
1960		screen->base64_pad += x;
1961		TRACE(("writing base64 interim %s\n", visibleChars(buf, x)));
1962		tty_vwrite(screen->respond, buf, x);
1963		x = 0;
1964	    }
1965	}
1966	if (x != 0) {
1967	    screen->base64_pad += x;
1968	    TRACE(("writing base64 finish %s\n", visibleChars(buf, x)));
1969	    tty_vwrite(screen->respond, buf, x);
1970	}
1971    } else
1972#endif /* OPT_PASTE64 */
1973#if OPT_READLINE
1974    if (SCREEN_FLAG(screen, paste_quotes)) {
1975	while (length--) {
1976	    tty_vwrite(screen->respond, (const Char *) "\026", 1);	/* Control-V */
1977	    tty_vwrite(screen->respond, lag++, 1);
1978	}
1979    } else
1980#endif
1981    {
1982	TRACE(("writing base64 padding %s\n", visibleChars(lag, length)));
1983	tty_vwrite(screen->respond, lag, length);
1984    }
1985}
1986
1987static void
1988_WriteSelectionData(XtermWidget xw, Char *line, size_t length)
1989{
1990    /* Write data to pty a line at a time. */
1991    /* Doing this one line at a time may no longer be necessary
1992       because v_write has been re-written. */
1993
1994#if OPT_PASTE64
1995    TScreen *screen = TScreenOf(xw);
1996#endif
1997    Char *lag, *end;
1998
1999    /* in the VMS version, if tt_pasting isn't set to True then qio
2000       reads aren't blocked and an infinite loop is entered, where the
2001       pasted text shows up as new input, goes in again, shows up
2002       again, ad nauseum. */
2003#ifdef VMS
2004    tt_pasting = True;
2005#endif
2006
2007    end = &line[length];
2008    lag = line;
2009
2010#if OPT_PASTE64
2011    if (screen->base64_paste) {
2012	_qWriteSelectionData(xw, lag, (unsigned) (end - lag));
2013	base64_flush(screen);
2014    } else
2015#endif
2016    {
2017	if (!SCREEN_FLAG(screen, paste_literal_nl)) {
2018	    Char *cp;
2019	    for (cp = line; cp != end; cp++) {
2020		if (*cp == '\n') {
2021		    *cp = '\r';
2022		    _qWriteSelectionData(xw, lag, (unsigned) (cp - lag + 1));
2023		    lag = cp + 1;
2024		}
2025	    }
2026	}
2027
2028	if (lag != end) {
2029	    _qWriteSelectionData(xw, lag, (unsigned) (end - lag));
2030	}
2031    }
2032#ifdef VMS
2033    tt_pasting = False;
2034    tt_start_read();		/* reenable reads or a character may be lost */
2035#endif
2036}
2037
2038#if OPT_READLINE
2039static void
2040_WriteKey(TScreen *screen, const Char *in)
2041{
2042    Char line[16];
2043    unsigned count = 0;
2044    size_t length = strlen((const char *) in);
2045
2046    if (screen->control_eight_bits) {
2047	line[count++] = ANSI_CSI;
2048    } else {
2049	line[count++] = ANSI_ESC;
2050	line[count++] = '[';
2051    }
2052    while (length--)
2053	line[count++] = *in++;
2054    line[count++] = '~';
2055    tty_vwrite(screen->respond, line, count);
2056}
2057#endif /* OPT_READLINE */
2058
2059/*
2060 * Unless enabled by the user, strip control characters other than formatting.
2061 */
2062static size_t
2063removeControls(XtermWidget xw, char *value)
2064{
2065    TScreen *screen = TScreenOf(xw);
2066    size_t dst = 0;
2067
2068    if (screen->allowPasteControls) {
2069	dst = strlen(value);
2070    } else {
2071	size_t src = 0;
2072	while ((value[dst] = value[src]) != '\0') {
2073	    int ch = CharOf(value[src++]);
2074	    if (ch < 32) {
2075		switch (ch) {
2076		case '\b':
2077		case '\t':
2078		case '\n':
2079		case '\r':
2080		    ++dst;
2081		    break;
2082		default:
2083		    continue;
2084		}
2085	    }
2086#if OPT_WIDE_CHARS
2087	    else if (screen->utf8_inparse || screen->utf8_nrc_mode)
2088		++dst;
2089#endif
2090#if OPT_C1_PRINT || OPT_WIDE_CHARS
2091	    else if (screen->c1_printable)
2092		++dst;
2093#endif
2094	    else if (ch >= 128 && ch < 160)
2095		continue;
2096	    else
2097		++dst;
2098	}
2099    }
2100    return dst;
2101}
2102
2103/* SelectionReceived: stuff received selection text into pty */
2104
2105/* ARGSUSED */
2106static void
2107SelectionReceived(Widget w,
2108		  XtPointer client_data,
2109		  Atom *selection GCC_UNUSED,
2110		  Atom *type,
2111		  XtPointer value,
2112		  unsigned long *length,
2113		  int *format)
2114{
2115    char **text_list = NULL;
2116    int text_list_count = 0;
2117    XTextProperty text_prop;
2118    TScreen *screen;
2119    Display *dpy;
2120#if OPT_TRACE && OPT_WIDE_CHARS
2121    Char *line = (Char *) value;
2122#endif
2123
2124    XtermWidget xw;
2125
2126    if ((xw = getXtermWidget(w)) == 0)
2127	return;
2128
2129    screen = TScreenOf(xw);
2130    dpy = XtDisplay(w);
2131
2132    if (*type == 0		/*XT_CONVERT_FAIL */
2133	|| *length == 0
2134	|| value == NULL) {
2135	TRACE(("...no data to convert\n"));
2136	goto fail;
2137    }
2138
2139    text_prop.value = (unsigned char *) value;
2140    text_prop.encoding = *type;
2141    text_prop.format = *format;
2142    text_prop.nitems = *length;
2143
2144    TRACE(("SelectionReceived %s %s format %d, nitems %ld\n",
2145	   TraceAtomName(screen->display, *selection),
2146	   visibleSelectionTarget(dpy, text_prop.encoding),
2147	   text_prop.format,
2148	   text_prop.nitems));
2149
2150#if OPT_WIDE_CHARS
2151    if (XSupportsLocale() && screen->wide_chars) {
2152	if (*type == XA_UTF8_STRING(dpy) ||
2153	    *type == XA_STRING ||
2154	    *type == XA_COMPOUND_TEXT(dpy)) {
2155	    GettingSelection(dpy, *type, line, *length);
2156	    if (Xutf8TextPropertyToTextList(dpy, &text_prop,
2157					    &text_list,
2158					    &text_list_count) < 0) {
2159		TRACE(("default Xutf8 Conversion failed\n"));
2160		text_list = NULL;
2161	    }
2162	}
2163    } else
2164#endif /* OPT_WIDE_CHARS */
2165    {
2166	/* Convert the selection to locale's multibyte encoding. */
2167
2168	if (*type == XA_UTF8_STRING(dpy) ||
2169	    *type == XA_STRING ||
2170	    *type == XA_COMPOUND_TEXT(dpy)) {
2171	    Status rc;
2172
2173	    GettingSelection(dpy, *type, line, *length);
2174
2175#if OPT_WIDE_CHARS
2176	    if (*type == XA_UTF8_STRING(dpy) &&
2177		!(screen->wide_chars || screen->c1_printable)) {
2178		rc = xtermUtf8ToTextList(xw, &text_prop,
2179					 &text_list, &text_list_count);
2180	    } else
2181#endif
2182	    if (*type == XA_STRING && (!XSupportsLocale() || screen->brokenSelections)) {
2183		rc = XTextPropertyToStringList(&text_prop,
2184					       &text_list, &text_list_count);
2185	    } else {
2186		rc = XmbTextPropertyToTextList(dpy, &text_prop,
2187					       &text_list,
2188					       &text_list_count);
2189	    }
2190	    if (rc < 0) {
2191		TRACE(("Conversion failed\n"));
2192		text_list = NULL;
2193	    }
2194	}
2195    }
2196
2197    if (text_list != NULL && text_list_count != 0) {
2198	int i;
2199
2200#if OPT_PASTE64
2201	if (screen->base64_paste) {
2202	    /* EMPTY */ ;
2203	} else
2204#endif
2205#if OPT_READLINE
2206	if (SCREEN_FLAG(screen, paste_brackets)) {
2207	    _WriteKey(screen, (const Char *) "200");
2208	}
2209#endif
2210	for (i = 0; i < text_list_count; i++) {
2211	    size_t len = removeControls(xw, text_list[i]);
2212
2213	    if (screen->selectToBuffer) {
2214		InternalSelect *mydata = &(screen->internal_select);
2215		size_t have = (mydata->buffer
2216			       ? strlen(mydata->buffer)
2217			       : 0);
2218		size_t need = have + len + 1;
2219		char *buffer = realloc(mydata->buffer, need);
2220
2221		screen->selectToBuffer = False;
2222#if OPT_PASTE64
2223		screen->base64_paste = mydata->base64_paste;
2224#endif
2225#if OPT_READLINE
2226		screen->paste_brackets = mydata->paste_brackets;
2227#endif
2228		if (buffer != 0) {
2229		    strcpy(buffer + have, text_list[i]);
2230		    mydata->buffer = buffer;
2231		}
2232		TRACE(("FormatSelect %d.%d .. %d.%d %s\n",
2233		       screen->startSel.row,
2234		       screen->startSel.col,
2235		       screen->endSel.row,
2236		       screen->endSel.col,
2237		       mydata->buffer));
2238		mydata->format_select(w, mydata->format, mydata->buffer,
2239				      &(screen->startSel),
2240				      &(screen->endSel));
2241
2242		free(mydata->format);
2243		free(mydata->buffer);
2244		memset(mydata, 0, sizeof(*mydata));
2245	    } else {
2246		_WriteSelectionData(xw, (Char *) text_list[i], len);
2247	    }
2248	}
2249#if OPT_PASTE64
2250	if (screen->base64_paste) {
2251	    FinishPaste64(xw);
2252	} else
2253#endif
2254#if OPT_READLINE
2255	if (SCREEN_FLAG(screen, paste_brackets)) {
2256	    _WriteKey(screen, (const Char *) "201");
2257	}
2258#endif
2259	XFreeStringList(text_list);
2260    } else {
2261	TRACE(("...empty text-list\n"));
2262	goto fail;
2263    }
2264
2265    XtFree((char *) client_data);
2266    XtFree((char *) value);
2267
2268    return;
2269
2270  fail:
2271    if (client_data != 0) {
2272	struct _SelectionList *list = (struct _SelectionList *) client_data;
2273
2274	TRACE(("SelectionReceived ->xtermGetSelection\n"));
2275	xtermGetSelection(w, list->time,
2276			  list->params, list->count, list->targets);
2277	XtFree((char *) client_data);
2278#if OPT_PASTE64
2279    } else {
2280	FinishPaste64(xw);
2281#endif
2282    }
2283    return;
2284}
2285
2286void
2287HandleInsertSelection(Widget w,
2288		      XEvent *event,	/* assumed to be XButtonEvent* */
2289		      String *params,	/* selections in precedence order */
2290		      Cardinal *num_params)
2291{
2292    XtermWidget xw;
2293
2294    if ((xw = getXtermWidget(w)) != 0) {
2295	TRACE(("HandleInsertSelection\n"));
2296	if (!SendMousePosition(xw, event)) {
2297#if OPT_READLINE
2298	    int ldelta;
2299	    TScreen *screen = TScreenOf(xw);
2300	    if (IsBtnEvent(event)
2301	    /* Disable on Shift-mouse, including the application-mouse modes */
2302		&& !(KeyModifiers(event) & ShiftMask)
2303		&& (okSendMousePos(xw) == MOUSE_OFF)
2304		&& SCREEN_FLAG(screen, paste_moves)
2305		&& rowOnCurrentLine(screen, eventRow(screen, event), &ldelta))
2306		ReadLineMovePoint(screen, eventColBetween(screen, event), ldelta);
2307#endif /* OPT_READLINE */
2308
2309	    xtermGetSelection(w, event->xbutton.time, params, *num_params, NULL);
2310	}
2311    }
2312}
2313
2314static SelectUnit
2315EvalSelectUnit(XtermWidget xw,
2316	       Time buttonDownTime,
2317	       SelectUnit defaultUnit,
2318	       unsigned int button)
2319{
2320    TScreen *screen = TScreenOf(xw);
2321    SelectUnit result;
2322    int delta;
2323
2324    if (button != screen->lastButton) {
2325	delta = screen->multiClickTime + 1;
2326    } else if (screen->lastButtonUpTime == (Time) 0) {
2327	/* first time and once in a blue moon */
2328	delta = screen->multiClickTime + 1;
2329    } else if (buttonDownTime > screen->lastButtonUpTime) {
2330	/* most of the time */
2331	delta = (int) (buttonDownTime - screen->lastButtonUpTime);
2332    } else {
2333	/* time has rolled over since lastButtonUpTime */
2334	delta = (int) ((((Time) ~ 0) - screen->lastButtonUpTime) + buttonDownTime);
2335    }
2336
2337    if (delta > screen->multiClickTime) {
2338	screen->numberOfClicks = 1;
2339	result = defaultUnit;
2340    } else {
2341	result = screen->selectMap[screen->numberOfClicks % screen->maxClicks];
2342	screen->numberOfClicks += 1;
2343    }
2344    TRACE(("EvalSelectUnit(%d) = %d\n", screen->numberOfClicks, result));
2345    return result;
2346}
2347
2348static void
2349do_select_start(XtermWidget xw,
2350		XEvent *event,	/* must be XButtonEvent* */
2351		CELL *cell)
2352{
2353    TScreen *screen = TScreenOf(xw);
2354
2355    if (SendMousePosition(xw, event))
2356	return;
2357    screen->selectUnit = EvalSelectUnit(xw,
2358					event->xbutton.time,
2359					Select_CHAR,
2360					event->xbutton.button);
2361    screen->replyToEmacs = False;
2362
2363#if OPT_READLINE
2364    lastButtonDownTime = event->xbutton.time;
2365#endif
2366
2367    StartSelect(xw, cell);
2368}
2369
2370/* ARGSUSED */
2371void
2372HandleSelectStart(Widget w,
2373		  XEvent *event,	/* must be XButtonEvent* */
2374		  String *params GCC_UNUSED,
2375		  Cardinal *num_params GCC_UNUSED)
2376{
2377    XtermWidget xw;
2378
2379    if ((xw = getXtermWidget(w)) != 0) {
2380	TScreen *screen = TScreenOf(xw);
2381	CELL cell;
2382
2383	TRACE(("HandleSelectStart\n"));
2384	screen->firstValidRow = 0;
2385	screen->lastValidRow = screen->max_row;
2386	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
2387
2388#if OPT_READLINE
2389	ExtendingSelection = 0;
2390#endif
2391
2392	do_select_start(xw, event, &cell);
2393    }
2394}
2395
2396/* ARGSUSED */
2397void
2398HandleKeyboardSelectStart(Widget w,
2399			  XEvent *event,	/* must be XButtonEvent* */
2400			  String *params GCC_UNUSED,
2401			  Cardinal *num_params GCC_UNUSED)
2402{
2403    XtermWidget xw;
2404
2405    if ((xw = getXtermWidget(w)) != 0) {
2406	TScreen *screen = TScreenOf(xw);
2407
2408	TRACE(("HandleKeyboardSelectStart\n"));
2409	do_select_start(xw, event, &screen->cursorp);
2410    }
2411}
2412
2413static void
2414TrackDown(XtermWidget xw, XButtonEvent *event)
2415{
2416    TScreen *screen = TScreenOf(xw);
2417    CELL cell;
2418
2419    screen->selectUnit = EvalSelectUnit(xw,
2420					event->time,
2421					Select_CHAR,
2422					event->button);
2423    if (screen->numberOfClicks > 1) {
2424	PointToCELL(screen, event->y, event->x, &cell);
2425	screen->replyToEmacs = True;
2426	StartSelect(xw, &cell);
2427    } else {
2428	screen->waitingForTrackInfo = True;
2429	EditorButton(xw, event);
2430    }
2431}
2432
2433#define boundsCheck(x)	if (x < 0) \
2434			    x = 0; \
2435			else if (x >= screen->max_row) \
2436			    x = screen->max_row
2437
2438void
2439TrackMouse(XtermWidget xw,
2440	   int func,
2441	   CELL *start,
2442	   int firstrow,
2443	   int lastrow)
2444{
2445    TScreen *screen = TScreenOf(xw);
2446
2447    if (screen->waitingForTrackInfo) {	/* if Timed, ignore */
2448	screen->waitingForTrackInfo = False;
2449
2450	if (func != 0) {
2451	    CELL first = *start;
2452
2453	    boundsCheck(first.row);
2454	    boundsCheck(firstrow);
2455	    boundsCheck(lastrow);
2456	    screen->firstValidRow = firstrow;
2457	    screen->lastValidRow = lastrow;
2458	    screen->replyToEmacs = True;
2459	    StartSelect(xw, &first);
2460	}
2461    }
2462}
2463
2464static void
2465StartSelect(XtermWidget xw, const CELL *cell)
2466{
2467    TScreen *screen = TScreenOf(xw);
2468
2469    TRACE(("StartSelect row=%d, col=%d\n", cell->row, cell->col));
2470    if (screen->cursor_state)
2471	HideCursor();
2472    if (screen->numberOfClicks == 1) {
2473	/* set start of selection */
2474	screen->rawPos = *cell;
2475    }
2476    /* else use old values in rawPos */
2477    screen->saveStartR = screen->startExt = screen->rawPos;
2478    screen->saveEndR = screen->endExt = screen->rawPos;
2479    if (Coordinate(screen, cell) < Coordinate(screen, &(screen->rawPos))) {
2480	screen->eventMode = LEFTEXTENSION;
2481	screen->startExt = *cell;
2482    } else {
2483	screen->eventMode = RIGHTEXTENSION;
2484	screen->endExt = *cell;
2485    }
2486    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False);
2487}
2488
2489static void
2490EndExtend(XtermWidget xw,
2491	  XEvent *event,	/* must be XButtonEvent */
2492	  String *params,	/* selections */
2493	  Cardinal num_params,
2494	  Bool use_cursor_loc)
2495{
2496    CELL cell;
2497    TScreen *screen = TScreenOf(xw);
2498
2499    if (use_cursor_loc) {
2500	cell = screen->cursorp;
2501    } else {
2502	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
2503    }
2504    ExtendExtend(xw, &cell);
2505
2506    screen->lastButtonUpTime = event->xbutton.time;
2507    screen->lastButton = event->xbutton.button;
2508
2509    if (!isSameCELL(&(screen->startSel), &(screen->endSel))) {
2510	if (screen->replyToEmacs) {
2511	    Char line[64];
2512	    unsigned count = 0;
2513
2514	    if (screen->control_eight_bits) {
2515		line[count++] = ANSI_CSI;
2516	    } else {
2517		line[count++] = ANSI_ESC;
2518		line[count++] = '[';
2519	    }
2520	    if (isSameCELL(&(screen->rawPos), &(screen->startSel))
2521		&& isSameCELL(&cell, &(screen->endSel))) {
2522		/* Use short-form emacs select */
2523
2524		switch (screen->extend_coords) {
2525		case 0:
2526		case SET_EXT_MODE_MOUSE:
2527		    line[count++] = 't';
2528		    break;
2529		case SET_SGR_EXT_MODE_MOUSE:
2530		    line[count++] = '<';
2531		    break;
2532		}
2533
2534		count = EmitMousePosition(screen, line, count, screen->endSel.col);
2535		count = EmitMousePositionSeparator(screen, line, count);
2536		count = EmitMousePosition(screen, line, count, screen->endSel.row);
2537
2538		switch (screen->extend_coords) {
2539		case SET_SGR_EXT_MODE_MOUSE:
2540		case SET_URXVT_EXT_MODE_MOUSE:
2541		    line[count++] = 't';
2542		    break;
2543		}
2544	    } else {
2545		/* long-form, specify everything */
2546
2547		switch (screen->extend_coords) {
2548		case 0:
2549		case SET_EXT_MODE_MOUSE:
2550		    line[count++] = 'T';
2551		    break;
2552		case SET_SGR_EXT_MODE_MOUSE:
2553		    line[count++] = '<';
2554		    break;
2555		}
2556
2557		count = EmitMousePosition(screen, line, count, screen->startSel.col);
2558		count = EmitMousePositionSeparator(screen, line, count);
2559		count = EmitMousePosition(screen, line, count, screen->startSel.row);
2560		count = EmitMousePositionSeparator(screen, line, count);
2561		count = EmitMousePosition(screen, line, count, screen->endSel.col);
2562		count = EmitMousePositionSeparator(screen, line, count);
2563		count = EmitMousePosition(screen, line, count, screen->endSel.row);
2564		count = EmitMousePositionSeparator(screen, line, count);
2565		count = EmitMousePosition(screen, line, count, cell.col);
2566		count = EmitMousePositionSeparator(screen, line, count);
2567		count = EmitMousePosition(screen, line, count, cell.row);
2568
2569		switch (screen->extend_coords) {
2570		case SET_SGR_EXT_MODE_MOUSE:
2571		case SET_URXVT_EXT_MODE_MOUSE:
2572		    line[count++] = 'T';
2573		    break;
2574		}
2575	    }
2576	    v_write(screen->respond, line, count);
2577	    TrackText(xw, &zeroCELL, &zeroCELL);
2578	}
2579    }
2580    SelectSet(xw, event, params, num_params);
2581    screen->eventMode = NORMAL;
2582}
2583
2584void
2585HandleSelectSet(Widget w,
2586		XEvent *event,
2587		String *params,
2588		Cardinal *num_params)
2589{
2590    XtermWidget xw;
2591
2592    if ((xw = getXtermWidget(w)) != 0) {
2593	TRACE(("HandleSelectSet\n"));
2594	SelectSet(xw, event, params, *num_params);
2595    }
2596}
2597
2598/* ARGSUSED */
2599static void
2600SelectSet(XtermWidget xw,
2601	  XEvent *event GCC_UNUSED,
2602	  String *params,
2603	  Cardinal num_params)
2604{
2605    TScreen *screen = TScreenOf(xw);
2606
2607    TRACE(("SelectSet\n"));
2608    /* Only do select stuff if non-null select */
2609    if (!isSameCELL(&(screen->startSel), &(screen->endSel))) {
2610	SaltTextAway(xw, &(screen->startSel), &(screen->endSel));
2611	_OwnSelection(xw, params, num_params);
2612    } else {
2613	ScrnDisownSelection(xw);
2614    }
2615}
2616
2617#define Abs(x)		((x) < 0 ? -(x) : (x))
2618
2619/* ARGSUSED */
2620static void
2621do_start_extend(XtermWidget xw,
2622		XEvent *event,	/* must be XButtonEvent* */
2623		String *params GCC_UNUSED,
2624		Cardinal *num_params GCC_UNUSED,
2625		Bool use_cursor_loc)
2626{
2627    TScreen *screen = TScreenOf(xw);
2628    int coord;
2629    CELL cell;
2630
2631    if (SendMousePosition(xw, event))
2632	return;
2633
2634    screen->firstValidRow = 0;
2635    screen->lastValidRow = screen->max_row;
2636#if OPT_READLINE
2637    if ((KeyModifiers(event) & ShiftMask)
2638	|| event->xbutton.button != Button3
2639	|| !(SCREEN_FLAG(screen, dclick3_deletes)))
2640#endif
2641	screen->selectUnit = EvalSelectUnit(xw,
2642					    event->xbutton.time,
2643					    screen->selectUnit,
2644					    event->xbutton.button);
2645    screen->replyToEmacs = False;
2646
2647#if OPT_READLINE
2648    CheckSecondPress3(screen, event);
2649#endif
2650
2651    if (screen->numberOfClicks == 1
2652	|| (SCREEN_FLAG(screen, dclick3_deletes)	/* Dclick special */
2653	    &&!(KeyModifiers(event) & ShiftMask))) {
2654	/* Save existing selection so we can reestablish it if the guy
2655	   extends past the other end of the selection */
2656	screen->saveStartR = screen->startExt = screen->startRaw;
2657	screen->saveEndR = screen->endExt = screen->endRaw;
2658    } else {
2659	/* He just needed the selection mode changed, use old values. */
2660	screen->startExt = screen->startRaw = screen->saveStartR;
2661	screen->endExt = screen->endRaw = screen->saveEndR;
2662    }
2663    if (use_cursor_loc) {
2664	cell = screen->cursorp;
2665    } else {
2666	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
2667    }
2668    coord = Coordinate(screen, &cell);
2669
2670    if (Abs(coord - Coordinate(screen, &(screen->startSel)))
2671	< Abs(coord - Coordinate(screen, &(screen->endSel)))
2672	|| coord < Coordinate(screen, &(screen->startSel))) {
2673	/* point is close to left side of selection */
2674	screen->eventMode = LEFTEXTENSION;
2675	screen->startExt = cell;
2676    } else {
2677	/* point is close to left side of selection */
2678	screen->eventMode = RIGHTEXTENSION;
2679	screen->endExt = cell;
2680    }
2681    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), True);
2682
2683#if OPT_READLINE
2684    if (!isSameCELL(&(screen->startSel), &(screen->endSel)))
2685	ExtendingSelection = 1;
2686#endif
2687}
2688
2689static void
2690ExtendExtend(XtermWidget xw, const CELL *cell)
2691{
2692    TScreen *screen = TScreenOf(xw);
2693    int coord = Coordinate(screen, cell);
2694
2695    TRACE(("ExtendExtend row=%d, col=%d\n", cell->row, cell->col));
2696    if (screen->eventMode == LEFTEXTENSION
2697	&& ((coord + (screen->selectUnit != Select_CHAR))
2698	    > Coordinate(screen, &(screen->endSel)))) {
2699	/* Whoops, he's changed his mind.  Do RIGHTEXTENSION */
2700	screen->eventMode = RIGHTEXTENSION;
2701	screen->startExt = screen->saveStartR;
2702    } else if (screen->eventMode == RIGHTEXTENSION
2703	       && coord < Coordinate(screen, &(screen->startSel))) {
2704	/* Whoops, he's changed his mind.  Do LEFTEXTENSION */
2705	screen->eventMode = LEFTEXTENSION;
2706	screen->endExt = screen->saveEndR;
2707    }
2708    if (screen->eventMode == LEFTEXTENSION) {
2709	screen->startExt = *cell;
2710    } else {
2711	screen->endExt = *cell;
2712    }
2713    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False);
2714
2715#if OPT_READLINE
2716    if (!isSameCELL(&(screen->startSel), &(screen->endSel)))
2717	ExtendingSelection = 1;
2718#endif
2719}
2720
2721void
2722HandleStartExtend(Widget w,
2723		  XEvent *event,	/* must be XButtonEvent* */
2724		  String *params,	/* unused */
2725		  Cardinal *num_params)		/* unused */
2726{
2727    XtermWidget xw;
2728
2729    if ((xw = getXtermWidget(w)) != 0) {
2730	TRACE(("HandleStartExtend\n"));
2731	do_start_extend(xw, event, params, num_params, False);
2732    }
2733}
2734
2735void
2736HandleKeyboardStartExtend(Widget w,
2737			  XEvent *event,	/* must be XButtonEvent* */
2738			  String *params,	/* unused */
2739			  Cardinal *num_params)		/* unused */
2740{
2741    XtermWidget xw;
2742
2743    if ((xw = getXtermWidget(w)) != 0) {
2744	TRACE(("HandleKeyboardStartExtend\n"));
2745	do_start_extend(xw, event, params, num_params, True);
2746    }
2747}
2748
2749void
2750ScrollSelection(TScreen *screen, int amount, Bool always)
2751{
2752    int minrow = INX2ROW(screen, -screen->savedlines);
2753    int maxrow = INX2ROW(screen, screen->max_row);
2754    int maxcol = screen->max_col;
2755
2756#define scroll_update_one(cell) \
2757	(cell)->row += amount; \
2758	if ((cell)->row < minrow) { \
2759	    (cell)->row = minrow; \
2760	    (cell)->col = 0; \
2761	} \
2762	if ((cell)->row > maxrow) { \
2763	    (cell)->row = maxrow; \
2764	    (cell)->col = maxcol; \
2765	}
2766
2767    scroll_update_one(&(screen->startRaw));
2768    scroll_update_one(&(screen->endRaw));
2769    scroll_update_one(&(screen->startSel));
2770    scroll_update_one(&(screen->endSel));
2771
2772    scroll_update_one(&(screen->rawPos));
2773
2774    /*
2775     * If we are told to scroll the selection but it lies outside the scrolling
2776     * margins, then that could cause the selection to move (bad).  It is not
2777     * simple to fix, because this function is called both for the scrollbar
2778     * actions as well as application scrolling.  The 'always' flag is set in
2779     * the former case.  The rest of the logic handles the latter.
2780     */
2781    if (ScrnHaveSelection(screen)) {
2782	int adjust;
2783
2784	adjust = ROW2INX(screen, screen->startH.row);
2785	if (always
2786	    || !ScrnHaveRowMargins(screen)
2787	    || ScrnIsRowInMargins(screen, adjust)) {
2788	    scroll_update_one(&screen->startH);
2789	}
2790	adjust = ROW2INX(screen, screen->endH.row);
2791	if (always
2792	    || !ScrnHaveRowMargins(screen)
2793	    || ScrnIsRowInMargins(screen, adjust)) {
2794	    scroll_update_one(&screen->endH);
2795	}
2796    }
2797
2798    screen->startHCoord = Coordinate(screen, &screen->startH);
2799    screen->endHCoord = Coordinate(screen, &screen->endH);
2800}
2801
2802/*ARGSUSED*/
2803void
2804ResizeSelection(TScreen *screen GCC_UNUSED, int rows, int cols)
2805{
2806    rows--;			/* decr to get 0-max */
2807    cols--;
2808
2809    if (screen->startRaw.row > rows)
2810	screen->startRaw.row = rows;
2811    if (screen->startSel.row > rows)
2812	screen->startSel.row = rows;
2813    if (screen->endRaw.row > rows)
2814	screen->endRaw.row = rows;
2815    if (screen->endSel.row > rows)
2816	screen->endSel.row = rows;
2817    if (screen->rawPos.row > rows)
2818	screen->rawPos.row = rows;
2819
2820    if (screen->startRaw.col > cols)
2821	screen->startRaw.col = cols;
2822    if (screen->startSel.col > cols)
2823	screen->startSel.col = cols;
2824    if (screen->endRaw.col > cols)
2825	screen->endRaw.col = cols;
2826    if (screen->endSel.col > cols)
2827	screen->endSel.col = cols;
2828    if (screen->rawPos.col > cols)
2829	screen->rawPos.col = cols;
2830}
2831
2832#if OPT_WIDE_CHARS
2833Bool
2834iswide(int i)
2835{
2836    return (i == HIDDEN_CHAR) || (WideCells(i) == 2);
2837}
2838
2839#define isWideCell(row, col) iswide((int)XTERM_CELL(row, col))
2840#endif
2841
2842static void
2843PointToCELL(TScreen *screen,
2844	    int y,
2845	    int x,
2846	    CELL *cell)
2847/* Convert pixel coordinates to character coordinates.
2848   Rows are clipped between firstValidRow and lastValidRow.
2849   Columns are clipped between to be 0 or greater, but are not clipped to some
2850       maximum value. */
2851{
2852    cell->row = (y - screen->border) / FontHeight(screen);
2853    if (cell->row < screen->firstValidRow)
2854	cell->row = screen->firstValidRow;
2855    else if (cell->row > screen->lastValidRow)
2856	cell->row = screen->lastValidRow;
2857    cell->col = (x - OriginX(screen)) / FontWidth(screen);
2858    if (cell->col < 0)
2859	cell->col = 0;
2860    else if (cell->col > MaxCols(screen)) {
2861	cell->col = MaxCols(screen);
2862    }
2863#if OPT_WIDE_CHARS
2864    /*
2865     * If we got a click on the right half of a doublewidth character,
2866     * pretend it happened on the left half.
2867     */
2868    if (cell->col > 0
2869	&& isWideCell(cell->row, cell->col - 1)
2870	&& (XTERM_CELL(cell->row, cell->col) == HIDDEN_CHAR)) {
2871	cell->col -= 1;
2872    }
2873#endif
2874}
2875
2876/*
2877 * Find the last column at which text was drawn on the given row.
2878 */
2879static int
2880LastTextCol(TScreen *screen, CLineData *ld, int row)
2881{
2882    int i = -1;
2883
2884    if (ld != 0) {
2885	if (okScrnRow(screen, row)) {
2886	    const IAttr *ch;
2887	    for (i = screen->max_col,
2888		 ch = ld->attribs + i;
2889		 i >= 0 && !(*ch & CHARDRAWN);
2890		 ch--, i--) {
2891		;
2892	    }
2893#if OPT_DEC_CHRSET
2894	    if (CSET_DOUBLE(GetLineDblCS(ld))) {
2895		i *= 2;
2896	    }
2897#endif
2898	}
2899    }
2900    return (i);
2901}
2902
2903#if !OPT_WIDE_CHARS
2904/*
2905** double click table for cut and paste in 8 bits
2906**
2907** This table is divided in four parts :
2908**
2909**	- control characters	[0,0x1f] U [0x80,0x9f]
2910**	- separators		[0x20,0x3f] U [0xa0,0xb9]
2911**	- binding characters	[0x40,0x7f] U [0xc0,0xff]
2912**	- exceptions
2913*/
2914/* *INDENT-OFF* */
2915static int charClass[256] =
2916{
2917/* NUL  SOH  STX  ETX  EOT  ENQ  ACK  BEL */
2918    32,  1,    1,   1,   1,   1,   1,   1,
2919/*  BS   HT   NL   VT   FF   CR   SO   SI */
2920     1,  32,   1,   1,   1,   1,   1,   1,
2921/* DLE  DC1  DC2  DC3  DC4  NAK  SYN  ETB */
2922     1,   1,   1,   1,   1,   1,   1,   1,
2923/* CAN   EM  SUB  ESC   FS   GS   RS   US */
2924     1,   1,   1,   1,   1,   1,   1,   1,
2925/*  SP    !    "    #    $    %    &    ' */
2926    32,  33,  34,  35,  36,  37,  38,  39,
2927/*   (    )    *    +    ,    -    .    / */
2928    40,  41,  42,  43,  44,  45,  46,  47,
2929/*   0    1    2    3    4    5    6    7 */
2930    48,  48,  48,  48,  48,  48,  48,  48,
2931/*   8    9    :    ;    <    =    >    ? */
2932    48,  48,  58,  59,  60,  61,  62,  63,
2933/*   @    A    B    C    D    E    F    G */
2934    64,  48,  48,  48,  48,  48,  48,  48,
2935/*   H    I    J    K    L    M    N    O */
2936    48,  48,  48,  48,  48,  48,  48,  48,
2937/*   P    Q    R    S    T    U    V    W */
2938    48,  48,  48,  48,  48,  48,  48,  48,
2939/*   X    Y    Z    [    \    ]    ^    _ */
2940    48,  48,  48,  91,  92,  93,  94,  48,
2941/*   `    a    b    c    d    e    f    g */
2942    96,  48,  48,  48,  48,  48,  48,  48,
2943/*   h    i    j    k    l    m    n    o */
2944    48,  48,  48,  48,  48,  48,  48,  48,
2945/*   p    q    r    s    t    u    v    w */
2946    48,  48,  48,  48,  48,  48,  48,  48,
2947/*   x    y    z    {    |    }    ~  DEL */
2948    48,  48,  48, 123, 124, 125, 126,   1,
2949/* x80  x81  x82  x83  IND  NEL  SSA  ESA */
2950    1,    1,   1,   1,   1,   1,   1,   1,
2951/* HTS  HTJ  VTS  PLD  PLU   RI  SS2  SS3 */
2952    1,    1,   1,   1,   1,   1,   1,   1,
2953/* DCS  PU1  PU2  STS  CCH   MW  SPA  EPA */
2954    1,    1,   1,   1,   1,   1,   1,   1,
2955/* x98  x99  x9A  CSI   ST  OSC   PM  APC */
2956    1,    1,   1,   1,   1,   1,   1,   1,
2957/*   -    i   c/    L   ox   Y-    |   So */
2958    160, 161, 162, 163, 164, 165, 166, 167,
2959/*  ..   c0   ip   <<    _        R0    - */
2960    168, 169, 170, 171, 172, 173, 174, 175,
2961/*   o   +-    2    3    '    u   q|    . */
2962    176, 177, 178, 179, 180, 181, 182, 183,
2963/*   ,    1    2   >>  1/4  1/2  3/4    ? */
2964    184, 185, 186, 187, 188, 189, 190, 191,
2965/*  A`   A'   A^   A~   A:   Ao   AE   C, */
2966     48,  48,  48,  48,  48,  48,  48,  48,
2967/*  E`   E'   E^   E:   I`   I'   I^   I: */
2968     48,  48,  48,  48,  48,  48,  48,  48,
2969/*  D-   N~   O`   O'   O^   O~   O:    X */
2970     48,  48,  48,  48,  48,  48,  48, 215,
2971/*  O/   U`   U'   U^   U:   Y'    P    B */
2972     48,  48,  48,  48,  48,  48,  48,  48,
2973/*  a`   a'   a^   a~   a:   ao   ae   c, */
2974     48,  48,  48,  48,  48,  48,  48,  48,
2975/*  e`   e'   e^   e:    i`  i'   i^   i: */
2976     48,  48,  48,  48,  48,  48,  48,  48,
2977/*   d   n~   o`   o'   o^   o~   o:   -: */
2978     48,  48,  48,  48,  48,  48,  48, 247,
2979/*  o/   u`   u'   u^   u:   y'    P   y: */
2980     48,  48,  48,  48,  48,  48,  48,  48};
2981/* *INDENT-ON* */
2982
2983int
2984SetCharacterClassRange(int low,	/* in range of [0..255] */
2985		       int high,
2986		       int value)	/* arbitrary */
2987{
2988
2989    if (low < 0 || high > 255 || high < low)
2990	return (-1);
2991
2992    for (; low <= high; low++)
2993	charClass[low] = value;
2994
2995    return (0);
2996}
2997#endif
2998
2999static int
3000class_of(LineData *ld, CELL *cell)
3001{
3002    CELL temp = *cell;
3003    int result = 0;
3004
3005#if OPT_DEC_CHRSET
3006    if (CSET_DOUBLE(GetLineDblCS(ld))) {
3007	temp.col /= 2;
3008    }
3009#endif
3010    if (temp.col < (int) ld->lineSize)
3011	result = CharacterClass((int) (ld->charData[temp.col]));
3012    return result;
3013}
3014
3015#if OPT_WIDE_CHARS
3016#define CClassSelects(name, cclass) \
3017	 (CClassOf(name) == cclass \
3018	 || XTERM_CELL(screen->name.row, screen->name.col) == HIDDEN_CHAR)
3019#else
3020#define CClassSelects(name, cclass) \
3021	 (class_of(ld.name, &((screen->name))) == cclass)
3022#endif
3023
3024#define CClassOf(name) class_of(ld.name, &((screen->name)))
3025
3026#if OPT_REPORT_CCLASS
3027static int
3028show_cclass_range(int lo, int hi)
3029{
3030    int cclass = CharacterClass(lo);
3031    int ident = (cclass == lo);
3032    int more = 0;
3033    if (ident) {
3034	int ch;
3035	for (ch = lo + 1; ch <= hi; ch++) {
3036	    if (CharacterClass(ch) != ch) {
3037		ident = 0;
3038		break;
3039	    }
3040	}
3041	if (ident && (hi < 255)) {
3042	    ch = hi + 1;
3043	    if (CharacterClass(ch) == ch) {
3044		if (ch >= 255 || CharacterClass(ch + 1) != ch) {
3045		    more = 1;
3046		}
3047	    }
3048	}
3049    }
3050    if (!more) {
3051	if (lo == hi) {
3052	    printf("\t%d", lo);
3053	} else {
3054	    printf("\t%d-%d", lo, hi);
3055	}
3056	if (!ident)
3057	    printf(":%d", cclass);
3058	if (hi < 255)
3059	    printf(", \\");
3060	printf("\n");
3061    }
3062    return !more;
3063}
3064
3065void
3066report_char_class(XtermWidget xw)
3067{
3068    /* simple table, to match documentation */
3069    static const char charnames[] =
3070    "NUL\0" "SOH\0" "STX\0" "ETX\0" "EOT\0" "ENQ\0" "ACK\0" "BEL\0"
3071    " BS\0" " HT\0" " NL\0" " VT\0" " NP\0" " CR\0" " SO\0" " SI\0"
3072    "DLE\0" "DC1\0" "DC2\0" "DC3\0" "DC4\0" "NAK\0" "SYN\0" "ETB\0"
3073    "CAN\0" " EM\0" "SUB\0" "ESC\0" " FS\0" " GS\0" " RS\0" " US\0"
3074    " SP\0" "  !\0" "  \"\0" "  #\0" "  $\0" "  %\0" "  &\0" "  '\0"
3075    "  (\0" "  )\0" "  *\0" "  +\0" "  ,\0" "  -\0" "  .\0" "  /\0"
3076    "  0\0" "  1\0" "  2\0" "  3\0" "  4\0" "  5\0" "  6\0" "  7\0"
3077    "  8\0" "  9\0" "  :\0" "  ;\0" "  <\0" "  =\0" "  >\0" "  ?\0"
3078    "  @\0" "  A\0" "  B\0" "  C\0" "  D\0" "  E\0" "  F\0" "  G\0"
3079    "  H\0" "  I\0" "  J\0" "  K\0" "  L\0" "  M\0" "  N\0" "  O\0"
3080    "  P\0" "  Q\0" "  R\0" "  S\0" "  T\0" "  U\0" "  V\0" "  W\0"
3081    "  X\0" "  Y\0" "  Z\0" "  [\0" "  \\\0" "  ]\0" "  ^\0" "  _\0"
3082    "  `\0" "  a\0" "  b\0" "  c\0" "  d\0" "  e\0" "  f\0" "  g\0"
3083    "  h\0" "  i\0" "  j\0" "  k\0" "  l\0" "  m\0" "  n\0" "  o\0"
3084    "  p\0" "  q\0" "  r\0" "  s\0" "  t\0" "  u\0" "  v\0" "  w\0"
3085    "  x\0" "  y\0" "  z\0" "  {\0" "  |\0" "  }\0" "  ~\0" "DEL\0"
3086    "x80\0" "x81\0" "x82\0" "x83\0" "IND\0" "NEL\0" "SSA\0" "ESA\0"
3087    "HTS\0" "HTJ\0" "VTS\0" "PLD\0" "PLU\0" " RI\0" "SS2\0" "SS3\0"
3088    "DCS\0" "PU1\0" "PU2\0" "STS\0" "CCH\0" " MW\0" "SPA\0" "EPA\0"
3089    "x98\0" "x99\0" "x9A\0" "CSI\0" " ST\0" "OSC\0" " PM\0" "APC\0"
3090    "  -\0" "  i\0" " c/\0" "  L\0" " ox\0" " Y-\0" "  |\0" " So\0"
3091    " ..\0" " c0\0" " ip\0" " <<\0" "  _\0" "   \0" " R0\0" "  -\0"
3092    "  o\0" " +-\0" "  2\0" "  3\0" "  '\0" "  u\0" " q|\0" "  .\0"
3093    "  ,\0" "  1\0" "  2\0" " >>\0" "1/4\0" "1/2\0" "3/4\0" "  ?\0"
3094    " A`\0" " A'\0" " A^\0" " A~\0" " A:\0" " Ao\0" " AE\0" " C,\0"
3095    " E`\0" " E'\0" " E^\0" " E:\0" " I`\0" " I'\0" " I^\0" " I:\0"
3096    " D-\0" " N~\0" " O`\0" " O'\0" " O^\0" " O~\0" " O:\0" "  X\0"
3097    " O/\0" " U`\0" " U'\0" " U^\0" " U:\0" " Y'\0" "  P\0" "  B\0"
3098    " a`\0" " a'\0" " a^\0" " a~\0" " a:\0" " ao\0" " ae\0" " c,\0"
3099    " e`\0" " e'\0" " e^\0" " e:\0" " i`\0" " i'\0" " i^\0" " i:\0"
3100    "  d\0" " n~\0" " o`\0" " o'\0" " o^\0" " o~\0" " o:\0" " -:\0"
3101    " o/\0" " u`\0" " u'\0" " u^\0" " u:\0" " y'\0" "  P\0" " y:\0";
3102    int ch, dh;
3103    int class_p;
3104
3105    (void) xw;
3106
3107    printf("static int charClass[256] = {\n");
3108    for (ch = 0; ch < 256; ++ch) {
3109	const char *s = charnames + (ch * 4);
3110	if ((ch & 7) == 0)
3111	    printf("/*");
3112	printf(" %s ", s);
3113	if (((ch + 1) & 7) == 0) {
3114	    printf("*/\n  ");
3115	    for (dh = ch - 7; dh <= ch; ++dh) {
3116		printf(" %3d%s", CharacterClass(dh), dh == 255 ? "};" : ",");
3117	    }
3118	    printf("\n");
3119	}
3120    }
3121
3122    /* print the table as if it were the charClass resource */
3123    printf("\n");
3124    printf("The table is equivalent to this \"charClass\" resource:\n");
3125    class_p = CharacterClass(dh = 0);
3126    for (ch = 0; ch < 256; ++ch) {
3127	int class_c = CharacterClass(ch);
3128	if (class_c != class_p) {
3129	    if (show_cclass_range(dh, ch - 1)) {
3130		dh = ch;
3131		class_p = class_c;
3132	    }
3133	}
3134    }
3135    if (dh < 255) {
3136	show_cclass_range(dh, 255);
3137    }
3138
3139    if_OPT_WIDE_CHARS(TScreenOf(xw), {
3140	/* if this is a wide-character configuration, print all intervals */
3141	report_wide_char_class();
3142    });
3143}
3144#endif
3145
3146/*
3147 * If the given column is past the end of text on the given row, bump to the
3148 * beginning of the next line.
3149 */
3150static Boolean
3151okPosition(TScreen *screen,
3152	   LineData **ld,
3153	   CELL *cell)
3154{
3155    Boolean result = True;
3156
3157    if (cell->row > screen->max_row) {
3158	result = False;
3159    } else if (cell->col > (LastTextCol(screen, *ld, cell->row) + 1)) {
3160	if (cell->row < screen->max_row) {
3161	    cell->col = 0;
3162	    *ld = GET_LINEDATA(screen, ++cell->row);
3163	    result = False;
3164	}
3165    }
3166    return result;
3167}
3168
3169static void
3170trimLastLine(TScreen *screen,
3171	     LineData **ld,
3172	     CELL *last)
3173{
3174    if (screen->cutNewline && last->row < screen->max_row) {
3175	last->col = 0;
3176	*ld = GET_LINEDATA(screen, ++last->row);
3177    } else {
3178	last->col = LastTextCol(screen, *ld, last->row) + 1;
3179    }
3180}
3181
3182#if OPT_SELECT_REGEX
3183/*
3184 * Returns the first row of a wrapped line.
3185 */
3186static int
3187firstRowOfLine(TScreen *screen, int row, Bool visible)
3188{
3189    LineData *ld = 0;
3190    int limit = visible ? 0 : -screen->savedlines;
3191
3192    while (row > limit &&
3193	   (ld = GET_LINEDATA(screen, row - 1)) != 0 &&
3194	   LineTstWrapped(ld)) {
3195	--row;
3196    }
3197    return row;
3198}
3199
3200/*
3201 * Returns the last row of a wrapped line.
3202 */
3203static int
3204lastRowOfLine(TScreen *screen, int row)
3205{
3206    LineData *ld;
3207
3208    while (row < screen->max_row &&
3209	   (ld = GET_LINEDATA(screen, row)) != 0 &&
3210	   LineTstWrapped(ld)) {
3211	++row;
3212    }
3213    return row;
3214}
3215
3216/*
3217 * Returns the number of cells on the range of rows.
3218 */
3219static unsigned
3220lengthOfLines(TScreen *screen, int firstRow, int lastRow)
3221{
3222    unsigned length = 0;
3223    int n;
3224
3225    for (n = firstRow; n <= lastRow; ++n) {
3226	LineData *ld = GET_LINEDATA(screen, n);
3227	int value = LastTextCol(screen, ld, n);
3228	if (value >= 0)
3229	    length += (unsigned) (value + 1);
3230    }
3231    return length;
3232}
3233
3234/*
3235 * Make a copy of the wrapped-line which corresponds to the given row as a
3236 * string of bytes.  Construct an index for the columns from the beginning of
3237 * the line.
3238 */
3239static char *
3240make_indexed_text(TScreen *screen, int row, unsigned length, int *indexed)
3241{
3242    Char *result = 0;
3243    size_t need = (length + 1);
3244
3245    /*
3246     * Get a quick upper bound to the number of bytes needed, if the whole
3247     * string were UTF-8.
3248     */
3249    if_OPT_WIDE_CHARS(screen, {
3250	need *= ((screen->lineExtra + 1) * 6);
3251    });
3252
3253    if ((result = TypeCallocN(Char, need + 1)) != 0) {
3254	LineData *ld = GET_LINEDATA(screen, row);
3255	unsigned used = 0;
3256	Char *last = result;
3257
3258	do {
3259	    int col = 0;
3260	    int limit = LastTextCol(screen, ld, row);
3261
3262	    while (col <= limit) {
3263		Char *next = last;
3264		unsigned data = ld->charData[col];
3265
3266		assert(col < (int) ld->lineSize);
3267		/* some internal points may not be drawn */
3268		if (data == 0)
3269		    data = ' ';
3270
3271		if_WIDE_OR_NARROW(screen, {
3272		    next = convertToUTF8(last, data);
3273		}
3274		, {
3275		    *next++ = CharOf(data);
3276		});
3277
3278		if_OPT_WIDE_CHARS(screen, {
3279		    size_t off;
3280		    for_each_combData(off, ld) {
3281			data = ld->combData[off][col];
3282			if (data == 0)
3283			    break;
3284			next = convertToUTF8(next, data);
3285		    }
3286		});
3287
3288		indexed[used] = (int) (last - result);
3289		*next = 0;
3290		/* TRACE(("index[%d.%d] %d:%s\n", row, used, indexed[used], last)); */
3291		last = next;
3292		++used;
3293		++col;
3294		indexed[used] = (int) (next - result);
3295	    }
3296	} while (used < length &&
3297		 LineTstWrapped(ld) &&
3298		 (ld = GET_LINEDATA(screen, ++row)) != 0 &&
3299		 row < screen->max_row);
3300    }
3301    /* TRACE(("result:%s\n", result)); */
3302    return (char *) result;
3303}
3304
3305/*
3306 * Find the column given an offset into the character string by using the
3307 * index constructed in make_indexed_text().
3308 */
3309static int
3310indexToCol(int *indexed, int len, int off)
3311{
3312    int col = 0;
3313    while (indexed[col] < len) {
3314	if (indexed[col] >= off)
3315	    break;
3316	++col;
3317    }
3318    return col;
3319}
3320
3321/*
3322 * Given a row number, and a column offset from that (which may be wrapped),
3323 * set the cell to the actual row/column values.
3324 */
3325static void
3326columnToCell(TScreen *screen, int row, int col, CELL *cell)
3327{
3328    while (row < screen->max_row) {
3329	CLineData *ld = GET_LINEDATA(screen, row);
3330	int last = LastTextCol(screen, ld, row);
3331
3332	/* TRACE(("last(%d) = %d, have %d\n", row, last, col)); */
3333	if (col <= last) {
3334	    break;
3335	}
3336	/*
3337	 * Stop if the current row does not wrap (does not continue the current
3338	 * line).
3339	 */
3340	if (!LineTstWrapped(ld)) {
3341	    col = last + 1;
3342	    break;
3343	}
3344	col -= (last + 1);
3345	++row;
3346    }
3347    if (col < 0)
3348	col = 0;
3349    cell->row = row;
3350    cell->col = col;
3351}
3352
3353/*
3354 * Given a cell, find the corresponding column offset.
3355 */
3356static int
3357cellToColumn(TScreen *screen, CELL *cell)
3358{
3359    CLineData *ld = 0;
3360    int col = cell->col;
3361    int row = firstRowOfLine(screen, cell->row, False);
3362    while (row < cell->row) {
3363	ld = GET_LINEDATA(screen, row);
3364	col += LastTextCol(screen, ld, row++);
3365    }
3366#if OPT_DEC_CHRSET
3367    if (ld == 0)
3368	ld = GET_LINEDATA(screen, row);
3369    if (CSET_DOUBLE(GetLineDblCS(ld)))
3370	col /= 2;
3371#endif
3372    return col;
3373}
3374
3375static void
3376do_select_regex(TScreen *screen, CELL *startc, CELL *endc)
3377{
3378    LineData *ld = GET_LINEDATA(screen, startc->row);
3379    int inx = ((screen->numberOfClicks - 1) % screen->maxClicks);
3380    char *expr = screen->selectExpr[inx];
3381    regex_t preg;
3382    regmatch_t match;
3383
3384    TRACE(("Select_REGEX[%d]:%s\n", inx, NonNull(expr)));
3385    if (okPosition(screen, &ld, startc) && expr != 0) {
3386	if (regcomp(&preg, expr, REG_EXTENDED) == 0) {
3387	    int firstRow = firstRowOfLine(screen, startc->row, True);
3388	    int lastRow = lastRowOfLine(screen, firstRow);
3389	    unsigned size = lengthOfLines(screen, firstRow, lastRow);
3390	    int actual = cellToColumn(screen, startc);
3391	    int *indexed;
3392
3393	    TRACE(("regcomp ok rows %d..%d bytes %d\n",
3394		   firstRow, lastRow, size));
3395
3396	    if ((indexed = TypeCallocN(int, size + 1)) != 0) {
3397		char *search;
3398		if ((search = make_indexed_text(screen,
3399						firstRow,
3400						size,
3401						indexed)) != 0) {
3402		    int len = (int) strlen(search);
3403		    int col;
3404		    int best_col = -1;
3405		    int best_len = -1;
3406
3407		    startc->row = 0;
3408		    startc->col = 0;
3409		    endc->row = 0;
3410		    endc->col = 0;
3411
3412		    for (col = 0; indexed[col] < len; ++col) {
3413			if (regexec(&preg,
3414				    search + indexed[col],
3415				    (size_t) 1, &match, 0) == 0) {
3416			    int start_inx = (int) (match.rm_so + indexed[col]);
3417			    int finis_inx = (int) (match.rm_eo + indexed[col]);
3418			    int start_col = indexToCol(indexed, len, start_inx);
3419			    int finis_col = indexToCol(indexed, len, finis_inx);
3420
3421			    if (start_col <= actual &&
3422				actual <= finis_col) {
3423				int test = finis_col - start_col;
3424				if (best_len < test) {
3425				    best_len = test;
3426				    best_col = start_col;
3427				    TRACE(("match column %d len %d\n",
3428					   best_col,
3429					   best_len));
3430				}
3431			    }
3432			}
3433		    }
3434		    if (best_col >= 0) {
3435			int best_nxt = best_col + best_len;
3436			columnToCell(screen, firstRow, best_col, startc);
3437			columnToCell(screen, firstRow, best_nxt, endc);
3438			TRACE(("search::%s\n", search));
3439			TRACE(("indexed:%d..%d -> %d..%d\n",
3440			       best_col, best_nxt,
3441			       indexed[best_col],
3442			       indexed[best_nxt]));
3443			TRACE(("matched:%d:%s\n",
3444			       indexed[best_nxt] + 1 -
3445			       indexed[best_col],
3446			       visibleChars((Char *) (search + indexed[best_col]),
3447					    (unsigned) (indexed[best_nxt] +
3448							1 -
3449							indexed[best_col]))));
3450		    }
3451		    free(search);
3452		}
3453		free(indexed);
3454#if OPT_DEC_CHRSET
3455		if ((ld = GET_LINEDATA(screen, startc->row)) != 0) {
3456		    if (CSET_DOUBLE(GetLineDblCS(ld)))
3457			startc->col *= 2;
3458		}
3459		if ((ld = GET_LINEDATA(screen, endc->row)) != 0) {
3460		    if (CSET_DOUBLE(GetLineDblCS(ld)))
3461			endc->col *= 2;
3462		}
3463#endif
3464	    }
3465	    regfree(&preg);
3466	}
3467    }
3468}
3469#endif /* OPT_SELECT_REGEX */
3470
3471#define InitRow(name) \
3472	ld.name = GET_LINEDATA(screen, screen->name.row)
3473
3474#define NextRow(name) \
3475	ld.name = GET_LINEDATA(screen, ++screen->name.row)
3476
3477#define PrevRow(name) \
3478	ld.name = GET_LINEDATA(screen, --screen->name.row)
3479
3480#define MoreRows(name) \
3481	(screen->name.row < screen->max_row)
3482
3483#define isPrevWrapped(name) \
3484	(screen->name.row > 0 \
3485	   && (ltmp = GET_LINEDATA(screen, screen->name.row - 1)) != 0 \
3486	   && LineTstWrapped(ltmp))
3487
3488/*
3489 * sets startSel endSel
3490 * ensuring that they have legal values
3491 */
3492static void
3493ComputeSelect(XtermWidget xw,
3494	      CELL *startc,
3495	      CELL *endc,
3496	      Bool extend)
3497{
3498    TScreen *screen = TScreenOf(xw);
3499
3500    int cclass;
3501    CELL first = *startc;
3502    CELL last = *endc;
3503    Boolean ignored = False;
3504
3505    struct {
3506	LineData *startSel;
3507	LineData *endSel;
3508    } ld;
3509    LineData *ltmp;
3510
3511    TRACE(("ComputeSelect(startRow=%d, startCol=%d, endRow=%d, endCol=%d, %sextend)\n",
3512	   first.row, first.col,
3513	   last.row, last.col,
3514	   extend ? "" : "no"));
3515
3516#if OPT_WIDE_CHARS
3517    if (first.col > 1
3518	&& isWideCell(first.row, first.col - 1)
3519	&& XTERM_CELL(first.row, first.col - 0) == HIDDEN_CHAR) {
3520	TRACE(("Adjusting start. Changing downwards from %i.\n", first.col));
3521	first.col -= 1;
3522	if (last.col == (first.col + 1))
3523	    last.col--;
3524    }
3525
3526    if (last.col > 1
3527	&& isWideCell(last.row, last.col - 1)
3528	&& XTERM_CELL(last.row, last.col) == HIDDEN_CHAR) {
3529	last.col += 1;
3530    }
3531#endif
3532
3533    if (Coordinate(screen, &first) <= Coordinate(screen, &last)) {
3534	screen->startSel = screen->startRaw = first;
3535	screen->endSel = screen->endRaw = last;
3536    } else {			/* Swap them */
3537	screen->startSel = screen->startRaw = last;
3538	screen->endSel = screen->endRaw = first;
3539    }
3540
3541    InitRow(startSel);
3542    InitRow(endSel);
3543
3544    switch (screen->selectUnit) {
3545    case Select_CHAR:
3546	(void) okPosition(screen, &(ld.startSel), &(screen->startSel));
3547	(void) okPosition(screen, &(ld.endSel), &(screen->endSel));
3548	break;
3549
3550    case Select_WORD:
3551	TRACE(("Select_WORD\n"));
3552	if (okPosition(screen, &(ld.startSel), &(screen->startSel))) {
3553	    cclass = CClassOf(startSel);
3554	    do {
3555		--screen->startSel.col;
3556		if (screen->startSel.col < 0
3557		    && isPrevWrapped(startSel)) {
3558		    PrevRow(startSel);
3559		    screen->startSel.col = LastTextCol(screen, ld.startSel, screen->startSel.row);
3560		}
3561	    } while (screen->startSel.col >= 0
3562		     && CClassSelects(startSel, cclass));
3563	    ++screen->startSel.col;
3564	}
3565#if OPT_WIDE_CHARS
3566	if (screen->startSel.col
3567	    && XTERM_CELL(screen->startSel.row,
3568			  screen->startSel.col) == HIDDEN_CHAR)
3569	    screen->startSel.col++;
3570#endif
3571
3572	if (okPosition(screen, &(ld.endSel), &(screen->endSel))) {
3573	    int length = LastTextCol(screen, ld.endSel, screen->endSel.row);
3574	    cclass = CClassOf(endSel);
3575	    do {
3576		++screen->endSel.col;
3577		if (screen->endSel.col > length
3578		    && LineTstWrapped(ld.endSel)) {
3579		    if (!MoreRows(endSel))
3580			break;
3581		    screen->endSel.col = 0;
3582		    NextRow(endSel);
3583		    length = LastTextCol(screen, ld.endSel, screen->endSel.row);
3584		}
3585	    } while (screen->endSel.col <= length
3586		     && CClassSelects(endSel, cclass));
3587	    /* Word-select selects if pointing to any char in "word",
3588	     * especially note that it includes the last character in a word.
3589	     * So we do no --endSel.col and do special eol handling.
3590	     */
3591	    if (screen->endSel.col > length + 1
3592		&& MoreRows(endSel)) {
3593		screen->endSel.col = 0;
3594		NextRow(endSel);
3595	    }
3596	}
3597#if OPT_WIDE_CHARS
3598	if (screen->endSel.col
3599	    && XTERM_CELL(screen->endSel.row,
3600			  screen->endSel.col) == HIDDEN_CHAR)
3601	    screen->endSel.col++;
3602#endif
3603
3604	screen->saveStartW = screen->startSel;
3605	break;
3606
3607    case Select_LINE:
3608	TRACE(("Select_LINE\n"));
3609	while (LineTstWrapped(ld.endSel)
3610	       && MoreRows(endSel)) {
3611	    NextRow(endSel);
3612	}
3613	if (screen->cutToBeginningOfLine
3614	    || screen->startSel.row < screen->saveStartW.row) {
3615	    screen->startSel.col = 0;
3616	    while (isPrevWrapped(startSel)) {
3617		PrevRow(startSel);
3618	    }
3619	} else if (!extend) {
3620	    if ((first.row < screen->saveStartW.row)
3621		|| (isSameRow(&first, &(screen->saveStartW))
3622		    && first.col < screen->saveStartW.col)) {
3623		screen->startSel.col = 0;
3624		while (isPrevWrapped(startSel)) {
3625		    PrevRow(startSel);
3626		}
3627	    } else {
3628		screen->startSel = screen->saveStartW;
3629	    }
3630	}
3631	trimLastLine(screen, &(ld.endSel), &(screen->endSel));
3632	break;
3633
3634    case Select_GROUP:		/* paragraph */
3635	TRACE(("Select_GROUP\n"));
3636	if (okPosition(screen, &(ld.startSel), &(screen->startSel))) {
3637	    /* scan backward for beginning of group */
3638	    while (screen->startSel.row > 0 &&
3639		   (LastTextCol(screen, ld.startSel, screen->startSel.row -
3640				1) > 0 ||
3641		    isPrevWrapped(startSel))) {
3642		PrevRow(startSel);
3643	    }
3644	    screen->startSel.col = 0;
3645	    /* scan forward for end of group */
3646	    while (MoreRows(endSel) &&
3647		   (LastTextCol(screen, ld.endSel, screen->endSel.row + 1) >
3648		    0 ||
3649		    LineTstWrapped(ld.endSel))) {
3650		NextRow(endSel);
3651	    }
3652	    trimLastLine(screen, &(ld.endSel), &(screen->endSel));
3653	}
3654	break;
3655
3656    case Select_PAGE:		/* everything one can see */
3657	TRACE(("Select_PAGE\n"));
3658	screen->startSel.row = 0;
3659	screen->startSel.col = 0;
3660	screen->endSel.row = MaxRows(screen);
3661	screen->endSel.col = 0;
3662	break;
3663
3664    case Select_ALL:		/* counts scrollback if in normal screen */
3665	TRACE(("Select_ALL\n"));
3666	screen->startSel.row = -screen->savedlines;
3667	screen->startSel.col = 0;
3668	screen->endSel.row = MaxRows(screen);
3669	screen->endSel.col = 0;
3670	break;
3671
3672#if OPT_SELECT_REGEX
3673    case Select_REGEX:
3674	do_select_regex(screen, &(screen->startSel), &(screen->endSel));
3675	break;
3676#endif
3677
3678    case NSELECTUNITS:		/* always ignore */
3679	ignored = True;
3680	break;
3681    }
3682
3683    if (!ignored) {
3684	/* check boundaries */
3685	ScrollSelection(screen, 0, False);
3686	TrackText(xw, &(screen->startSel), &(screen->endSel));
3687    }
3688
3689    return;
3690}
3691
3692/* Guaranteed (first.row, first.col) <= (last.row, last.col) */
3693static void
3694TrackText(XtermWidget xw,
3695	  const CELL *firstp,
3696	  const CELL *lastp)
3697{
3698    TScreen *screen = TScreenOf(xw);
3699    int from, to;
3700    CELL old_start, old_end;
3701    CELL first = *firstp;
3702    CELL last = *lastp;
3703
3704    TRACE(("TrackText(first=%d,%d, last=%d,%d)\n",
3705	   first.row, first.col, last.row, last.col));
3706
3707    old_start = screen->startH;
3708    old_end = screen->endH;
3709    TRACE(("...previous(first=%d,%d, last=%d,%d)\n",
3710	   old_start.row, old_start.col,
3711	   old_end.row, old_end.col));
3712    if (isSameCELL(&first, &old_start) &&
3713	isSameCELL(&last, &old_end)) {
3714	return;
3715    }
3716
3717    screen->startH = first;
3718    screen->endH = last;
3719    from = Coordinate(screen, &screen->startH);
3720    to = Coordinate(screen, &screen->endH);
3721    if (to <= screen->startHCoord || from > screen->endHCoord) {
3722	/* No overlap whatsoever between old and new hilite */
3723	ReHiliteText(xw, &old_start, &old_end);
3724	ReHiliteText(xw, &first, &last);
3725    } else {
3726	if (from < screen->startHCoord) {
3727	    /* Extend left end */
3728	    ReHiliteText(xw, &first, &old_start);
3729	} else if (from > screen->startHCoord) {
3730	    /* Shorten left end */
3731	    ReHiliteText(xw, &old_start, &first);
3732	}
3733	if (to > screen->endHCoord) {
3734	    /* Extend right end */
3735	    ReHiliteText(xw, &old_end, &last);
3736	} else if (to < screen->endHCoord) {
3737	    /* Shorten right end */
3738	    ReHiliteText(xw, &last, &old_end);
3739	}
3740    }
3741    screen->startHCoord = from;
3742    screen->endHCoord = to;
3743}
3744
3745/* Guaranteed that (first->row, first->col) <= (last->row, last->col) */
3746static void
3747ReHiliteText(XtermWidget xw,
3748	     CELL *firstp,
3749	     CELL *lastp)
3750{
3751    TScreen *screen = TScreenOf(xw);
3752    CELL first = *firstp;
3753    CELL last = *lastp;
3754
3755    TRACE(("ReHiliteText from %d.%d to %d.%d\n",
3756	   first.row, first.col, last.row, last.col));
3757
3758    if (first.row < 0)
3759	first.row = first.col = 0;
3760    else if (first.row > screen->max_row)
3761	return;			/* nothing to do, since last.row >= first.row */
3762
3763    if (last.row < 0)
3764	return;			/* nothing to do, since first.row <= last.row */
3765    else if (last.row > screen->max_row) {
3766	last.row = screen->max_row;
3767	last.col = MaxCols(screen);
3768    }
3769    if (isSameCELL(&first, &last))
3770	return;
3771
3772    if (!isSameRow(&first, &last)) {	/* do multiple rows */
3773	int i;
3774	if ((i = screen->max_col - first.col + 1) > 0) {	/* first row */
3775	    ScrnRefresh(xw, first.row, first.col, 1, i, True);
3776	}
3777	if ((i = last.row - first.row - 1) > 0) {	/* middle rows */
3778	    ScrnRefresh(xw, first.row + 1, 0, i, MaxCols(screen), True);
3779	}
3780	if (last.col > 0 && last.row <= screen->max_row) {	/* last row */
3781	    ScrnRefresh(xw, last.row, 0, 1, last.col, True);
3782	}
3783    } else {			/* do single row */
3784	ScrnRefresh(xw, first.row, first.col, 1, last.col - first.col, True);
3785    }
3786}
3787
3788/*
3789 * Guaranteed that (cellc->row, cellc->col) <= (cell->row, cell->col),
3790 * and that both points are valid
3791 * (may have cell->row = screen->max_row+1, cell->col = 0).
3792 */
3793static void
3794SaltTextAway(XtermWidget xw,
3795	     CELL *cellc,
3796	     CELL *cell)
3797{
3798    TScreen *screen = TScreenOf(xw);
3799    int i, j = 0;
3800    int eol;
3801    Char *line;
3802    Char *lp;
3803    CELL first = *cellc;
3804    CELL last = *cell;
3805
3806    if (isSameRow(&first, &last) && first.col > last.col) {
3807	int tmp;
3808	EXCHANGE(first.col, last.col, tmp);
3809    }
3810
3811    --last.col;
3812    /* first we need to know how long the string is before we can save it */
3813
3814    if (isSameRow(&last, &first)) {
3815	j = Length(screen, first.row, first.col, last.col);
3816    } else {			/* two cases, cut is on same line, cut spans multiple lines */
3817	j += Length(screen, first.row, first.col, screen->max_col) + 1;
3818	for (i = first.row + 1; i < last.row; i++)
3819	    j += Length(screen, i, 0, screen->max_col) + 1;
3820	if (last.col >= 0)
3821	    j += Length(screen, last.row, 0, last.col);
3822    }
3823
3824    /* UTF-8 may require more space */
3825    if_OPT_WIDE_CHARS(screen, {
3826	j *= 4;
3827    });
3828
3829    /* now get some memory to save it in */
3830
3831    if (screen->selection_size <= j) {
3832	if ((line = (Char *) malloc((size_t) j + 1)) == 0)
3833	    SysError(ERROR_BMALLOC2);
3834	XtFree((char *) screen->selection_data);
3835	screen->selection_data = line;
3836	screen->selection_size = j + 1;
3837    } else {
3838	line = screen->selection_data;
3839    }
3840
3841    if ((line == 0)
3842	|| (j < 0))
3843	return;
3844
3845    line[j] = '\0';		/* make sure it is null terminated */
3846    lp = line;			/* lp points to where to save the text */
3847    if (isSameRow(&last, &first)) {
3848	lp = SaveText(screen, last.row, first.col, last.col, lp, &eol);
3849    } else {
3850	lp = SaveText(screen, first.row, first.col, screen->max_col, lp, &eol);
3851	if (eol)
3852	    *lp++ = '\n';	/* put in newline at end of line */
3853	for (i = first.row + 1; i < last.row; i++) {
3854	    lp = SaveText(screen, i, 0, screen->max_col, lp, &eol);
3855	    if (eol)
3856		*lp++ = '\n';
3857	}
3858	if (last.col >= 0)
3859	    lp = SaveText(screen, last.row, 0, last.col, lp, &eol);
3860    }
3861    *lp = '\0';			/* make sure we have end marked */
3862
3863    TRACE(("Salted TEXT:%d:%s\n", (int) (lp - line),
3864	   visibleChars(line, (unsigned) (lp - line))));
3865
3866    screen->selection_length = (unsigned long) (lp - line);
3867}
3868
3869#if OPT_PASTE64
3870void
3871ClearSelectionBuffer(TScreen *screen)
3872{
3873    screen->selection_length = 0;
3874    screen->base64_count = 0;
3875}
3876
3877static void
3878AppendStrToSelectionBuffer(TScreen *screen, Char *text, size_t len)
3879{
3880    if (len != 0) {
3881	int j = (int) (screen->selection_length + len);		/* New length */
3882	int k = j + (j >> 2) + 80;	/* New size if we grow buffer: grow by ~50% */
3883	if (j + 1 >= screen->selection_size) {
3884	    if (!screen->selection_length) {
3885		/* New buffer */
3886		Char *line;
3887		if ((line = (Char *) malloc((size_t) k)) == 0)
3888		    SysError(ERROR_BMALLOC2);
3889		XtFree((char *) screen->selection_data);
3890		screen->selection_data = line;
3891	    } else {
3892		/* Realloc buffer */
3893		screen->selection_data = (Char *)
3894		    realloc(screen->selection_data,
3895			    (size_t) k);
3896		if (screen->selection_data == 0)
3897		    SysError(ERROR_BMALLOC2);
3898	    }
3899	    screen->selection_size = k;
3900	}
3901	if (screen->selection_data != 0) {
3902	    memcpy(screen->selection_data + screen->selection_length, text, len);
3903	    screen->selection_length += len;
3904	    screen->selection_data[screen->selection_length] = 0;
3905	}
3906    }
3907}
3908
3909void
3910AppendToSelectionBuffer(TScreen *screen, unsigned c)
3911{
3912    unsigned six;
3913    Char ch;
3914
3915    /* Decode base64 character */
3916    if (c >= 'A' && c <= 'Z')
3917	six = c - 'A';
3918    else if (c >= 'a' && c <= 'z')
3919	six = c - 'a' + 26;
3920    else if (c >= '0' && c <= '9')
3921	six = c - '0' + 52;
3922    else if (c == '+')
3923	six = 62;
3924    else if (c == '/')
3925	six = 63;
3926    else
3927	return;
3928
3929    /* Accumulate bytes */
3930    switch (screen->base64_count) {
3931    case 0:
3932	screen->base64_accu = six;
3933	screen->base64_count = 6;
3934	break;
3935
3936    case 2:
3937	ch = CharOf((screen->base64_accu << 6) + six);
3938	screen->base64_count = 0;
3939	AppendStrToSelectionBuffer(screen, &ch, (size_t) 1);
3940	break;
3941
3942    case 4:
3943	ch = CharOf((screen->base64_accu << 4) + (six >> 2));
3944	screen->base64_accu = (six & 0x3);
3945	screen->base64_count = 2;
3946	AppendStrToSelectionBuffer(screen, &ch, (size_t) 1);
3947	break;
3948
3949    case 6:
3950	ch = CharOf((screen->base64_accu << 2) + (six >> 4));
3951	screen->base64_accu = (six & 0xF);
3952	screen->base64_count = 4;
3953	AppendStrToSelectionBuffer(screen, &ch, (size_t) 1);
3954	break;
3955    }
3956}
3957
3958void
3959CompleteSelection(XtermWidget xw, String *args, Cardinal len)
3960{
3961    TScreen *screen = TScreenOf(xw);
3962
3963    screen->base64_count = 0;
3964    screen->base64_accu = 0;
3965    _OwnSelection(xw, args, len);
3966}
3967#endif /* OPT_PASTE64 */
3968
3969static Bool
3970_ConvertSelectionHelper(Widget w,
3971			Atom *type,
3972			XtPointer *value,
3973			unsigned long *length,
3974			Char *data,
3975			unsigned long remaining,
3976			int *format,
3977			int (*conversion_function) (Display *,
3978						    char **, int,
3979						    XICCEncodingStyle,
3980						    XTextProperty *),
3981			XICCEncodingStyle conversion_style)
3982{
3983    XtermWidget xw;
3984
3985    *value = 0;
3986    *length = 0;
3987    *type = 0;
3988    *format = 0;
3989
3990    if ((xw = getXtermWidget(w)) != 0) {
3991	TScreen *screen = TScreenOf(xw);
3992	Display *dpy = XtDisplay(w);
3993	XTextProperty textprop;
3994	int out_n = 0;
3995	char *result = 0;
3996	char *the_data = (char *) data;
3997	char *the_next;
3998
3999	TRACE(("converting %ld:'%s'\n",
4000	       (long) screen->selection_length,
4001	       visibleChars(screen->selection_data, (unsigned) screen->selection_length)));
4002	/*
4003	 * For most selections, we can convert in one pass.  It is possible
4004	 * that some applications contain embedded nulls, e.g., using xterm's
4005	 * paste64 feature.  For those cases, we will build up the result in
4006	 * parts.
4007	 */
4008	if (memchr(the_data, 0, screen->selection_length) != 0) {
4009	    TRACE(("selection contains embedded nulls\n"));
4010	    result = calloc(screen->selection_length + 1, sizeof(char));
4011	}
4012
4013      next_try:
4014	memset(&textprop, 0, sizeof(textprop));
4015	if (conversion_function(dpy, &the_data, 1,
4016				conversion_style,
4017				&textprop) >= Success) {
4018	    if ((result != 0)
4019		&& (textprop.value != 0)
4020		&& (textprop.format == 8)) {
4021		char *text_values = (char *) textprop.value;
4022		unsigned long in_n;
4023
4024		if (out_n == 0) {
4025		    *value = result;
4026		    *type = textprop.encoding;
4027		    *format = textprop.format;
4028		}
4029		for (in_n = 0; in_n < textprop.nitems; ++in_n) {
4030		    result[out_n++] = text_values[in_n];
4031		}
4032		*length += textprop.nitems;
4033		if ((the_next = memchr(the_data, 0, remaining)) != 0) {
4034		    unsigned long this_was = (unsigned long) (the_next - the_data);
4035		    this_was++;
4036		    the_data += this_was;
4037		    remaining -= this_was;
4038		    result[out_n++] = 0;
4039		    *length += 1;
4040		    if (remaining)
4041			goto next_try;
4042		}
4043		return True;
4044	    } else {
4045		free(result);
4046		*value = (XtPointer) textprop.value;
4047		*length = textprop.nitems;
4048		*type = textprop.encoding;
4049		*format = textprop.format;
4050		return True;
4051	    }
4052	}
4053	free(result);
4054    }
4055    return False;
4056}
4057
4058static Boolean
4059SaveConvertedLength(XtPointer *target, unsigned long source)
4060{
4061    Boolean result = False;
4062
4063    *target = XtMalloc(4);
4064    if (*target != 0) {
4065	result = True;
4066	if (sizeof(unsigned long) == 4) {
4067	    *(unsigned long *) *target = source;
4068	} else if (sizeof(unsigned) == 4) {
4069	    *(unsigned *) *target = (unsigned) source;
4070	} else if (sizeof(unsigned short) == 4) {
4071	    *(unsigned short *) *target = (unsigned short) source;
4072	} else {
4073	    /* FIXME - does this depend on byte-order? */
4074	    unsigned long temp = source;
4075	    memcpy((char *) *target,
4076		   ((char *) &temp) + sizeof(temp) - 4,
4077		   (size_t) 4);
4078	}
4079    }
4080    return result;
4081}
4082
4083#define keepClipboard(atom) ((screen->keepClipboard) && \
4084	 (atom == XInternAtom(screen->display, "CLIPBOARD", False)))
4085
4086static Boolean
4087ConvertSelection(Widget w,
4088		 Atom *selection,
4089		 Atom *target,
4090		 Atom *type,
4091		 XtPointer *value,
4092		 unsigned long *length,
4093		 int *format)
4094{
4095    Display *dpy = XtDisplay(w);
4096    TScreen *screen;
4097    Bool result = False;
4098
4099    Char *data;
4100    unsigned long data_length;
4101
4102    XtermWidget xw;
4103
4104    if ((xw = getXtermWidget(w)) == 0)
4105	return False;
4106
4107    screen = TScreenOf(xw);
4108
4109    TRACE(("ConvertSelection %s\n",
4110	   visibleSelectionTarget(dpy, *target)));
4111
4112    if (keepClipboard(*selection)) {
4113	TRACE(("asked for clipboard\n"));
4114	data = screen->clipboard_data;
4115	data_length = screen->clipboard_size;
4116    } else {
4117	TRACE(("asked for selection\n"));
4118	data = screen->selection_data;
4119	data_length = screen->selection_length;
4120    }
4121
4122    if (data == NULL) {
4123	TRACE(("...FIXME: no selection_data\n"));
4124	return False;		/* can this happen? */
4125    }
4126
4127    if (*target == XA_TARGETS(dpy)) {
4128	Atom *targetP;
4129	XPointer std_return = 0;
4130	unsigned long std_length;
4131
4132	if (XmuConvertStandardSelection(w, screen->selection_time, selection,
4133					target, type, &std_return,
4134					&std_length, format)) {
4135	    Atom *my_targets = _SelectionTargets(w);
4136	    Atom *allocP;
4137	    Atom *std_targets;
4138
4139	    TRACE(("XmuConvertStandardSelection - success\n"));
4140	    std_targets = (Atom *) (void *) (std_return);
4141	    *length = std_length + 6;
4142
4143	    targetP = TypeXtMallocN(Atom, *length);
4144	    allocP = targetP;
4145
4146	    *value = (XtPointer) targetP;
4147
4148	    if (my_targets != 0) {
4149		while (*my_targets != None) {
4150		    *targetP++ = *my_targets++;
4151		}
4152	    }
4153	    *targetP++ = XA_LENGTH(dpy);
4154	    *targetP++ = XA_LIST_LENGTH(dpy);
4155
4156	    *length = std_length + (unsigned long) (targetP - allocP);
4157
4158	    memcpy(targetP, std_targets, sizeof(Atom) * std_length);
4159	    XtFree((char *) std_targets);
4160	    *type = XA_ATOM;
4161	    *format = 32;
4162	    result = True;
4163	} else {
4164	    TRACE(("XmuConvertStandardSelection - failed\n"));
4165	}
4166    }
4167#if OPT_WIDE_CHARS
4168    else if (screen->wide_chars && *target == XA_STRING) {
4169	result =
4170	    _ConvertSelectionHelper(w,
4171				    type, value, length, data,
4172				    data_length, format,
4173				    Xutf8TextListToTextProperty,
4174				    XStringStyle);
4175	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4176    } else if (screen->wide_chars && *target == XA_UTF8_STRING(dpy)) {
4177	result =
4178	    _ConvertSelectionHelper(w,
4179				    type, value, length, data,
4180				    data_length, format,
4181				    Xutf8TextListToTextProperty,
4182				    XUTF8StringStyle);
4183	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4184    } else if (screen->wide_chars && *target == XA_TEXT(dpy)) {
4185	result =
4186	    _ConvertSelectionHelper(w,
4187				    type, value, length, data,
4188				    data_length, format,
4189				    Xutf8TextListToTextProperty,
4190				    XStdICCTextStyle);
4191	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4192    } else if (screen->wide_chars && *target == XA_COMPOUND_TEXT(dpy)) {
4193	result =
4194	    _ConvertSelectionHelper(w,
4195				    type, value, length, data,
4196				    data_length, format,
4197				    Xutf8TextListToTextProperty,
4198				    XCompoundTextStyle);
4199	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4200    }
4201#endif
4202
4203    else if (*target == XA_STRING) {	/* not wide_chars */
4204	/* We can only reach this point if the selection requestor
4205	   requested STRING before any of TEXT, COMPOUND_TEXT or
4206	   UTF8_STRING.  We therefore assume that the requestor is not
4207	   properly internationalised, and dump raw eight-bit data
4208	   with no conversion into the selection.  Yes, this breaks
4209	   the ICCCM in non-Latin-1 locales. */
4210	*type = XA_STRING;
4211	*value = (XtPointer) screen->selection_data;
4212	*length = screen->selection_length;
4213	*format = 8;
4214	result = True;
4215	TRACE(("...raw 8-bit data:%d\n", result));
4216    } else if (*target == XA_TEXT(dpy)) {	/* not wide_chars */
4217	result =
4218	    _ConvertSelectionHelper(w,
4219				    type, value, length, data,
4220				    data_length, format,
4221				    XmbTextListToTextProperty,
4222				    XStdICCTextStyle);
4223	TRACE(("...XmbTextListToTextProperty(StdICC):%d\n", result));
4224    } else if (*target == XA_COMPOUND_TEXT(dpy)) {	/* not wide_chars */
4225	result =
4226	    _ConvertSelectionHelper(w,
4227				    type, value, length, data,
4228				    data_length, format,
4229				    XmbTextListToTextProperty,
4230				    XCompoundTextStyle);
4231	TRACE(("...XmbTextListToTextProperty(Compound):%d\n", result));
4232    }
4233#ifdef X_HAVE_UTF8_STRING
4234    else if (*target == XA_UTF8_STRING(dpy)) {	/* not wide_chars */
4235	result =
4236	    _ConvertSelectionHelper(w,
4237				    type, value, length, data,
4238				    data_length, format,
4239				    XmbTextListToTextProperty,
4240				    XUTF8StringStyle);
4241	TRACE(("...XmbTextListToTextProperty(UTF8):%d\n", result));
4242    }
4243#endif
4244    else if (*target == XA_LIST_LENGTH(dpy)) {
4245	result = SaveConvertedLength(value, (unsigned long) 1);
4246	*type = XA_INTEGER;
4247	*length = 1;
4248	*format = 32;
4249	TRACE(("...list of values:%d\n", result));
4250    } else if (*target == XA_LENGTH(dpy)) {
4251	/* This value is wrong if we have UTF-8 text */
4252	result = SaveConvertedLength(value, screen->selection_length);
4253	*type = XA_INTEGER;
4254	*length = 1;
4255	*format = 32;
4256	TRACE(("...list of values:%d\n", result));
4257    } else if (XmuConvertStandardSelection(w,
4258					   screen->selection_time, selection,
4259					   target, type, (XPointer *) value,
4260					   length, format)) {
4261	result = True;
4262	TRACE(("...XmuConvertStandardSelection:%d\n", result));
4263    }
4264
4265    /* else */
4266    return (Boolean) result;
4267}
4268
4269static void
4270LoseSelection(Widget w, Atom *selection)
4271{
4272    TScreen *screen;
4273    Atom *atomP;
4274    Cardinal i;
4275
4276    XtermWidget xw;
4277
4278    if ((xw = getXtermWidget(w)) == 0)
4279	return;
4280
4281    screen = TScreenOf(xw);
4282    TRACE(("LoseSelection %s\n", TraceAtomName(screen->display, *selection)));
4283
4284    for (i = 0, atomP = screen->selection_atoms;
4285	 i < screen->selection_count; i++, atomP++) {
4286	if (*selection == *atomP)
4287	    *atomP = (Atom) 0;
4288	if (CutBuffer(*atomP) >= 0) {
4289	    *atomP = (Atom) 0;
4290	}
4291    }
4292
4293    for (i = screen->selection_count; i; i--) {
4294	if (screen->selection_atoms[i - 1] != 0)
4295	    break;
4296    }
4297    screen->selection_count = i;
4298
4299    for (i = 0, atomP = screen->selection_atoms;
4300	 i < screen->selection_count; i++, atomP++) {
4301	if (*atomP == (Atom) 0) {
4302	    *atomP = screen->selection_atoms[--screen->selection_count];
4303	}
4304    }
4305
4306    if (screen->selection_count == 0)
4307	TrackText(xw, &zeroCELL, &zeroCELL);
4308}
4309
4310/* ARGSUSED */
4311static void
4312SelectionDone(Widget w GCC_UNUSED,
4313	      Atom *selection GCC_UNUSED,
4314	      Atom *target GCC_UNUSED)
4315{
4316    /* empty proc so Intrinsics know we want to keep storage */
4317    TRACE(("SelectionDone\n"));
4318}
4319
4320static void
4321_OwnSelection(XtermWidget xw,
4322	      String *selections,
4323	      Cardinal count)
4324{
4325    TScreen *screen = TScreenOf(xw);
4326    Atom *atoms = screen->selection_atoms;
4327    Cardinal i;
4328    Bool have_selection = False;
4329
4330    if (count == 0)
4331	return;
4332
4333    TRACE(("_OwnSelection count %d, length %ld value %s\n", count,
4334	   screen->selection_length,
4335	   visibleChars(screen->selection_data, (unsigned) screen->selection_length)));
4336    selections = MapSelections(xw, selections, count);
4337
4338    if (count > screen->sel_atoms_size) {
4339	XtFree((char *) atoms);
4340	atoms = TypeXtMallocN(Atom, count);
4341	screen->selection_atoms = atoms;
4342	screen->sel_atoms_size = count;
4343    }
4344    XmuInternStrings(XtDisplay((Widget) xw), selections, count, atoms);
4345    for (i = 0; i < count; i++) {
4346	int cutbuffer = CutBuffer(atoms[i]);
4347	if (cutbuffer >= 0) {
4348	    unsigned long limit =
4349	    (unsigned long) (4 * XMaxRequestSize(XtDisplay((Widget) xw)) - 32);
4350	    if (screen->selection_length > limit) {
4351		TRACE(("selection too big (%lu bytes), not storing in CUT_BUFFER%d\n",
4352		       screen->selection_length, cutbuffer));
4353		xtermWarning("selection too big (%lu bytes), not storing in CUT_BUFFER%d\n",
4354			     screen->selection_length, cutbuffer);
4355	    } else {
4356		/* This used to just use the UTF-8 data, which was totally
4357		 * broken as not even the corresponding paste code in xterm
4358		 * understood this!  So now it converts to Latin1 first.
4359		 *   Robert Brady, 2000-09-05
4360		 */
4361		unsigned long length = screen->selection_length;
4362		Char *data = screen->selection_data;
4363		if_OPT_WIDE_CHARS((screen), {
4364		    data = UTF8toLatin1(screen, data, length, &length);
4365		});
4366		TRACE(("XStoreBuffer(%d)\n", cutbuffer));
4367		XStoreBuffer(XtDisplay((Widget) xw),
4368			     (char *) data,
4369			     (int) length,
4370			     cutbuffer);
4371	    }
4372	} else if (keepClipboard(atoms[i])) {
4373	    Char *buf;
4374	    TRACE(("saving selection to clipboard buffer\n"));
4375	    if ((buf = (Char *) malloc((size_t) screen->selection_length))
4376		== 0)
4377		SysError(ERROR_BMALLOC2);
4378
4379	    XtFree((char *) screen->clipboard_data);
4380	    memcpy(buf, screen->selection_data, screen->selection_length);
4381	    screen->clipboard_data = buf;
4382	    screen->clipboard_size = screen->selection_length;
4383	} else if (screen->selection_length == 0) {
4384	    XtDisownSelection((Widget) xw, atoms[i], screen->selection_time);
4385	} else if (!screen->replyToEmacs) {
4386	    have_selection |=
4387		XtOwnSelection((Widget) xw, atoms[i],
4388			       screen->selection_time,
4389			       ConvertSelection, LoseSelection, SelectionDone);
4390	}
4391    }
4392    if (!screen->replyToEmacs)
4393	screen->selection_count = count;
4394    if (!have_selection)
4395	TrackText(xw, &zeroCELL, &zeroCELL);
4396}
4397
4398static void
4399ResetSelectionState(TScreen *screen)
4400{
4401    screen->selection_count = 0;
4402    screen->startH = zeroCELL;
4403    screen->endH = zeroCELL;
4404}
4405
4406void
4407DisownSelection(XtermWidget xw)
4408{
4409    TScreen *screen = TScreenOf(xw);
4410    Atom *atoms = screen->selection_atoms;
4411    Cardinal count = screen->selection_count;
4412    Cardinal i;
4413
4414    TRACE(("DisownSelection count %d, start %d.%d, end %d.%d\n",
4415	   count,
4416	   screen->startH.row,
4417	   screen->startH.col,
4418	   screen->endH.row,
4419	   screen->endH.col));
4420
4421    for (i = 0; i < count; i++) {
4422	int cutbuffer = CutBuffer(atoms[i]);
4423	if (cutbuffer < 0) {
4424	    XtDisownSelection((Widget) xw, atoms[i],
4425			      screen->selection_time);
4426	}
4427    }
4428    /*
4429     * If none of the callbacks via XtDisownSelection() reset highlighting
4430     * do it now.
4431     */
4432    if (ScrnHaveSelection(screen)) {
4433	/* save data which will be reset */
4434	CELL first = screen->startH;
4435	CELL last = screen->endH;
4436
4437	ResetSelectionState(screen);
4438	ReHiliteText(xw, &first, &last);
4439    } else {
4440	ResetSelectionState(screen);
4441    }
4442}
4443
4444void
4445UnhiliteSelection(XtermWidget xw)
4446{
4447    TScreen *screen = TScreenOf(xw);
4448
4449    if (ScrnHaveSelection(screen)) {
4450	CELL first = screen->startH;
4451	CELL last = screen->endH;
4452
4453	screen->startH = zeroCELL;
4454	screen->endH = zeroCELL;
4455	ReHiliteText(xw, &first, &last);
4456    }
4457}
4458
4459/* returns number of chars in line from scol to ecol out */
4460/* ARGSUSED */
4461static int
4462Length(TScreen *screen,
4463       int row,
4464       int scol,
4465       int ecol)
4466{
4467    CLineData *ld = GET_LINEDATA(screen, row);
4468    const int lastcol = LastTextCol(screen, ld, row);
4469
4470    if (ecol > lastcol)
4471	ecol = lastcol;
4472    return (ecol - scol + 1);
4473}
4474
4475/* copies text into line, preallocated */
4476static Char *
4477SaveText(TScreen *screen,
4478	 int row,
4479	 int scol,
4480	 int ecol,
4481	 Char *lp,		/* pointer to where to put the text */
4482	 int *eol)
4483{
4484    LineData *ld;
4485    int i = 0;
4486    Char *result = lp;
4487#if OPT_WIDE_CHARS
4488    unsigned previous = 0;
4489#endif
4490
4491    ld = GET_LINEDATA(screen, row);
4492    i = Length(screen, row, scol, ecol);
4493    ecol = scol + i;
4494#if OPT_DEC_CHRSET
4495    if (CSET_DOUBLE(GetLineDblCS(ld))) {
4496	scol = (scol + 0) / 2;
4497	ecol = (ecol + 1) / 2;
4498    }
4499#endif
4500    *eol = !LineTstWrapped(ld);
4501    for (i = scol; i < ecol; i++) {
4502	unsigned c;
4503	assert(i < (int) ld->lineSize);
4504	c = E2A(ld->charData[i]);
4505#if OPT_WIDE_CHARS
4506	/* We want to strip out every occurrence of HIDDEN_CHAR AFTER a
4507	 * wide character.
4508	 */
4509	if (c == HIDDEN_CHAR) {
4510	    if (isWide((int) previous)) {
4511		previous = c;
4512		/* Combining characters attached to double-width characters
4513		   are in memory attached to the HIDDEN_CHAR */
4514		if_OPT_WIDE_CHARS(screen, {
4515		    if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
4516			size_t off;
4517			for_each_combData(off, ld) {
4518			    unsigned ch = ld->combData[off][i];
4519			    if (ch == 0)
4520				break;
4521			    lp = convertToUTF8(lp, ch);
4522			}
4523		    }
4524		});
4525		continue;
4526	    } else {
4527		c = ' ';	/* should not happen, but just in case... */
4528	    }
4529	}
4530	previous = c;
4531	if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
4532	    lp = convertToUTF8(lp, (c != 0) ? c : ' ');
4533	    if_OPT_WIDE_CHARS(screen, {
4534		size_t off;
4535		for_each_combData(off, ld) {
4536		    unsigned ch = ld->combData[off][i];
4537		    if (ch == 0)
4538			break;
4539		    lp = convertToUTF8(lp, ch);
4540		}
4541	    });
4542	} else
4543#endif
4544	{
4545	    if (c == 0) {
4546		c = E2A(' ');
4547	    } else if (c < E2A(' ')) {
4548		c = DECtoASCII(c);
4549	    } else if (c == 0x7f) {
4550		c = 0x5f;
4551	    }
4552	    *lp++ = CharOf(A2E(c));
4553	}
4554	if (c != E2A(' '))
4555	    result = lp;
4556    }
4557
4558    /*
4559     * If requested, trim trailing blanks from selected lines.  Do not do this
4560     * if the line is wrapped.
4561     */
4562    if (!*eol || !screen->trim_selection)
4563	result = lp;
4564
4565    return (result);
4566}
4567
4568/* 32 + following 7-bit word:
4569
4570   1:0  Button no: 0, 1, 2.  3=release.
4571     2  shift
4572     3  meta
4573     4  ctrl
4574     5  set for motion notify
4575     6  set for wheel
4576*/
4577
4578/* Position: 32 - 255. */
4579static int
4580BtnCode(XButtonEvent *event, int button)
4581{
4582    int result = (int) (32 + (KeyState(event->state) << 2));
4583
4584    if (event->type == MotionNotify)
4585	result += 32;
4586
4587    if (button < 0 || button > 5) {
4588	result += 3;
4589    } else {
4590	if (button > 3)
4591	    result += (64 - 4);
4592	result += button;
4593    }
4594    TRACE(("BtnCode button %d, %s state " FMT_MODIFIER_NAMES " ->%#x\n",
4595	   button,
4596	   visibleEventType(event->type),
4597	   ARG_MODIFIER_NAMES(event->state),
4598	   result));
4599    return result;
4600}
4601
4602static unsigned
4603EmitButtonCode(XtermWidget xw,
4604	       Char *line,
4605	       unsigned count,
4606	       XButtonEvent *event,
4607	       int button)
4608{
4609    TScreen *screen = TScreenOf(xw);
4610    int value;
4611
4612    if (okSendMousePos(xw) == X10_MOUSE) {
4613	value = CharOf(' ' + button);
4614    } else {
4615	value = BtnCode(event, button);
4616    }
4617
4618    switch (screen->extend_coords) {
4619    default:
4620	line[count++] = CharOf(value);
4621	break;
4622    case SET_SGR_EXT_MODE_MOUSE:
4623	value -= 32;		/* encoding starts at zero */
4624	/* FALLTHRU */
4625    case SET_URXVT_EXT_MODE_MOUSE:
4626	count += (unsigned) sprintf((char *) line + count, "%d", value);
4627	break;
4628    case SET_EXT_MODE_MOUSE:
4629	if (value < 128) {
4630	    line[count++] = CharOf(value);
4631	} else {
4632	    line[count++] = CharOf(0xC0 + (value >> 6));
4633	    line[count++] = CharOf(0x80 + (value & 0x3F));
4634	}
4635	break;
4636    }
4637    return count;
4638}
4639
4640static int
4641FirstBitN(int bits)
4642{
4643    int result = -1;
4644    if (bits > 0) {
4645	result = 0;
4646	while (!(bits & 1)) {
4647	    bits /= 2;
4648	    ++result;
4649	}
4650    }
4651    return result;
4652}
4653
4654#define ButtonBit(button) ((button >= 0) ? (1 << (button)) : 0)
4655
4656#define EMIT_BUTTON(button) EmitButtonCode(xw, line, count, event, button)
4657
4658static void
4659EditorButton(XtermWidget xw, XButtonEvent *event)
4660{
4661    TScreen *screen = TScreenOf(xw);
4662    int pty = screen->respond;
4663    int mouse_limit = MouseLimit(screen);
4664    Char line[32];
4665    Char final = 'M';
4666    int row, col;
4667    int button;
4668    unsigned count = 0;
4669    Boolean changed = True;
4670
4671    /* If button event, get button # adjusted for DEC compatibility */
4672    button = (int) (event->button - 1);
4673    if (button >= 3)
4674	button++;
4675
4676    /* Compute character position of mouse pointer */
4677    row = (event->y - screen->border) / FontHeight(screen);
4678    col = (event->x - OriginX(screen)) / FontWidth(screen);
4679
4680    /* Limit to screen dimensions */
4681    if (row < 0)
4682	row = 0;
4683    else if (row > screen->max_row)
4684	row = screen->max_row;
4685
4686    if (col < 0)
4687	col = 0;
4688    else if (col > screen->max_col)
4689	col = screen->max_col;
4690
4691    if (mouse_limit > 0) {
4692	/* Limit to representable mouse dimensions */
4693	if (row > mouse_limit)
4694	    row = mouse_limit;
4695	if (col > mouse_limit)
4696	    col = mouse_limit;
4697    }
4698
4699    /* Build key sequence starting with \E[M */
4700    if (screen->control_eight_bits) {
4701	line[count++] = ANSI_CSI;
4702    } else {
4703	line[count++] = ANSI_ESC;
4704	line[count++] = '[';
4705    }
4706    switch (screen->extend_coords) {
4707    case 0:
4708    case SET_EXT_MODE_MOUSE:
4709#if OPT_SCO_FUNC_KEYS
4710	if (xw->keyboard.type == keyboardIsSCO) {
4711	    /*
4712	     * SCO function key F1 is \E[M, which would conflict with xterm's
4713	     * normal kmous.
4714	     */
4715	    line[count++] = '>';
4716	}
4717#endif
4718	line[count++] = final;
4719	break;
4720    case SET_SGR_EXT_MODE_MOUSE:
4721	line[count++] = '<';
4722	break;
4723    }
4724
4725    /* Add event code to key sequence */
4726    if (okSendMousePos(xw) == X10_MOUSE) {
4727	count = EMIT_BUTTON(button);
4728    } else {
4729	/* Button-Motion events */
4730	switch (event->type) {
4731	case ButtonPress:
4732	    screen->mouse_button |= ButtonBit(button);
4733	    count = EMIT_BUTTON(button);
4734	    break;
4735	case ButtonRelease:
4736	    /*
4737	     * Wheel mouse interface generates release-events for buttons
4738	     * 4 and 5, coded here as 3 and 4 respectively.  We change the
4739	     * release for buttons 1..3 to a -1, which will be later mapped
4740	     * into a "0" (some button was released).
4741	     */
4742	    screen->mouse_button &= ~ButtonBit(button);
4743	    if (button < 3) {
4744		switch (screen->extend_coords) {
4745		case SET_SGR_EXT_MODE_MOUSE:
4746		    final = 'm';
4747		    break;
4748		default:
4749		    button = -1;
4750		    break;
4751		}
4752	    }
4753	    count = EMIT_BUTTON(button);
4754	    break;
4755	case MotionNotify:
4756	    /* BTN_EVENT_MOUSE and ANY_EVENT_MOUSE modes send motion
4757	     * events only if character cell has changed.
4758	     */
4759	    if ((row == screen->mouse_row)
4760		&& (col == screen->mouse_col)) {
4761		changed = False;
4762	    } else {
4763		count = EMIT_BUTTON(FirstBitN(screen->mouse_button));
4764	    }
4765	    break;
4766	default:
4767	    changed = False;
4768	    break;
4769	}
4770    }
4771
4772    if (changed) {
4773	screen->mouse_row = row;
4774	screen->mouse_col = col;
4775
4776	TRACE(("mouse at %d,%d button+mask = %#x\n", row, col, line[count - 1]));
4777
4778	/* Add pointer position to key sequence */
4779	count = EmitMousePositionSeparator(screen, line, count);
4780	count = EmitMousePosition(screen, line, count, col);
4781	count = EmitMousePositionSeparator(screen, line, count);
4782	count = EmitMousePosition(screen, line, count, row);
4783
4784	switch (screen->extend_coords) {
4785	case SET_SGR_EXT_MODE_MOUSE:
4786	case SET_URXVT_EXT_MODE_MOUSE:
4787	    line[count++] = final;
4788	    break;
4789	}
4790
4791	/* Transmit key sequence to process running under xterm */
4792	v_write(pty, line, count);
4793    }
4794    return;
4795}
4796
4797/*
4798 * Check the current send_mouse_pos against allowed mouse-operations, returning
4799 * none if it is disallowed.
4800 */
4801XtermMouseModes
4802okSendMousePos(XtermWidget xw)
4803{
4804    TScreen *screen = TScreenOf(xw);
4805    XtermMouseModes result = screen->send_mouse_pos;
4806
4807    switch (result) {
4808    case MOUSE_OFF:
4809	break;
4810    case X10_MOUSE:
4811	if (!AllowMouseOps(xw, emX10))
4812	    result = MOUSE_OFF;
4813	break;
4814    case VT200_MOUSE:
4815	if (!AllowMouseOps(xw, emVT200Click))
4816	    result = MOUSE_OFF;
4817	break;
4818    case VT200_HIGHLIGHT_MOUSE:
4819	if (!AllowMouseOps(xw, emVT200Hilite))
4820	    result = MOUSE_OFF;
4821	break;
4822    case BTN_EVENT_MOUSE:
4823	if (!AllowMouseOps(xw, emAnyButton))
4824	    result = MOUSE_OFF;
4825	break;
4826    case ANY_EVENT_MOUSE:
4827	if (!AllowMouseOps(xw, emAnyEvent))
4828	    result = MOUSE_OFF;
4829	break;
4830    case DEC_LOCATOR:
4831	if (!AllowMouseOps(xw, emLocator))
4832	    result = MOUSE_OFF;
4833	break;
4834    }
4835    return result;
4836}
4837
4838#if OPT_FOCUS_EVENT
4839/*
4840 * Check the current send_focus_pos against allowed mouse-operations, returning
4841 * none if it is disallowed.
4842 */
4843static int
4844okSendFocusPos(XtermWidget xw)
4845{
4846    TScreen *screen = TScreenOf(xw);
4847    int result = screen->send_focus_pos;
4848
4849    if (!AllowMouseOps(xw, emFocusEvent)) {
4850	result = False;
4851    }
4852    return result;
4853}
4854
4855void
4856SendFocusButton(XtermWidget xw, XFocusChangeEvent *event)
4857{
4858    if (okSendFocusPos(xw)) {
4859	ANSI reply;
4860
4861	memset(&reply, 0, sizeof(reply));
4862	reply.a_type = ANSI_CSI;
4863
4864#if OPT_SCO_FUNC_KEYS
4865	if (xw->keyboard.type == keyboardIsSCO) {
4866	    reply.a_pintro = '>';
4867	}
4868#endif
4869	reply.a_final = CharOf((event->type == FocusIn) ? 'I' : 'O');
4870	unparseseq(xw, &reply);
4871    }
4872    return;
4873}
4874#endif /* OPT_FOCUS_EVENT */
4875
4876#if OPT_SELECTION_OPS
4877/*
4878 * Get the event-time, needed to process selections.
4879 */
4880static Time
4881getEventTime(XEvent *event)
4882{
4883    Time result;
4884
4885    if (IsBtnEvent(event)) {
4886	result = ((XButtonEvent *) event)->time;
4887    } else if (IsKeyEvent(event)) {
4888	result = ((XKeyEvent *) event)->time;
4889    } else {
4890	result = 0;
4891    }
4892
4893    return result;
4894}
4895
4896/* obtain the selection string, passing the endpoints to caller's parameters */
4897static void
4898doSelectionFormat(XtermWidget xw,
4899		  Widget w,
4900		  XEvent *event,
4901		  String *params,
4902		  Cardinal *num_params,
4903		  FormatSelect format_select)
4904{
4905    TScreen *screen = TScreenOf(xw);
4906    InternalSelect *mydata = &(screen->internal_select);
4907
4908    memset(mydata, 0, sizeof(*mydata));
4909    mydata->format = x_strdup(params[0]);
4910    mydata->format_select = format_select;
4911
4912    /* override flags so that SelectionReceived only updates a buffer */
4913#if OPT_PASTE64
4914    mydata->base64_paste = screen->base64_paste;
4915    screen->base64_paste = 0;
4916#endif
4917#if OPT_READLINE
4918    mydata->paste_brackets = screen->paste_brackets;
4919    SCREEN_FLAG_unset(screen, paste_brackets);
4920#endif
4921
4922    screen->selectToBuffer = True;
4923    xtermGetSelection(w, getEventTime(event), params + 1, *num_params - 1, NULL);
4924}
4925
4926/* obtain data from the screen, passing the endpoints to caller's parameters */
4927static char *
4928getDataFromScreen(XtermWidget xw, XEvent *event, String method, CELL *start, CELL *finish)
4929{
4930    TScreen *screen = TScreenOf(xw);
4931
4932    CELL save_old_start = screen->startH;
4933    CELL save_old_end = screen->endH;
4934
4935    CELL save_startSel = screen->startSel;
4936    CELL save_startRaw = screen->startRaw;
4937    CELL save_finishSel = screen->endSel;
4938    CELL save_finishRaw = screen->endRaw;
4939
4940    int save_firstValidRow = screen->firstValidRow;
4941    int save_lastValidRow = screen->lastValidRow;
4942
4943    const Cardinal noClick = 0;
4944    int save_numberOfClicks = screen->numberOfClicks;
4945
4946    SelectUnit saveUnits = screen->selectUnit;
4947    SelectUnit saveMap = screen->selectMap[noClick];
4948#if OPT_SELECT_REGEX
4949    char *saveExpr = screen->selectExpr[noClick];
4950#endif
4951
4952    Char *save_selection_data = screen->selection_data;
4953    int save_selection_size = screen->selection_size;
4954    unsigned long save_selection_length = screen->selection_length;
4955
4956    char *result = 0;
4957
4958    TRACE(("getDataFromScreen %s\n", method));
4959
4960    screen->selection_data = 0;
4961    screen->selection_size = 0;
4962    screen->selection_length = 0;
4963
4964    screen->numberOfClicks = 1;
4965    lookupSelectUnit(xw, noClick, method);
4966    screen->selectUnit = screen->selectMap[noClick];
4967
4968    memset(start, 0, sizeof(*start));
4969    if (IsBtnEvent(event)) {
4970	XButtonEvent *btn_event = (XButtonEvent *) event;
4971	CELL cell;
4972	screen->firstValidRow = 0;
4973	screen->lastValidRow = screen->max_row;
4974	PointToCELL(screen, btn_event->y, btn_event->x, &cell);
4975	start->row = cell.row;
4976	start->col = cell.col;
4977	finish->row = cell.row;
4978	finish->col = screen->max_col;
4979    } else {
4980	start->row = screen->cur_row;
4981	start->col = screen->cur_col;
4982	finish->row = screen->cur_row;
4983	finish->col = screen->max_col;
4984    }
4985
4986    ComputeSelect(xw, start, finish, False);
4987    SaltTextAway(xw, &(screen->startSel), &(screen->endSel));
4988
4989    if (screen->selection_length && screen->selection_data) {
4990	TRACE(("...getDataFromScreen selection_data %.*s\n",
4991	       (int) screen->selection_length,
4992	       screen->selection_data));
4993	result = malloc(screen->selection_length + 1);
4994	if (result) {
4995	    memcpy(result, screen->selection_data, screen->selection_length);
4996	    result[screen->selection_length] = 0;
4997	}
4998	free(screen->selection_data);
4999    }
5000
5001    TRACE(("...getDataFromScreen restoring previous selection\n"));
5002
5003    screen->startSel = save_startSel;
5004    screen->startRaw = save_startRaw;
5005    screen->endSel = save_finishSel;
5006    screen->endRaw = save_finishRaw;
5007
5008    screen->firstValidRow = save_firstValidRow;
5009    screen->lastValidRow = save_lastValidRow;
5010
5011    screen->numberOfClicks = save_numberOfClicks;
5012    screen->selectUnit = saveUnits;
5013    screen->selectMap[noClick] = saveMap;
5014#if OPT_SELECT_REGEX
5015    screen->selectExpr[noClick] = saveExpr;
5016#endif
5017
5018    screen->selection_data = save_selection_data;
5019    screen->selection_size = save_selection_size;
5020    screen->selection_length = save_selection_length;
5021
5022    TrackText(xw, &save_old_start, &save_old_end);
5023
5024    TRACE(("...getDataFromScreen done\n"));
5025    return result;
5026}
5027
5028/*
5029 * Split-up the format before substituting data, to avoid quoting issues.
5030 * The resource mechanism has a limited ability to handle escapes.  We take
5031 * the result as if it were an sh-type string and parse it into a regular
5032 * argv array.
5033 */
5034static char **
5035tokenizeFormat(String format)
5036{
5037    char **result = 0;
5038    int argc;
5039
5040    format = x_skip_blanks(format);
5041    if (*format != '\0') {
5042	char *blob = x_strdup(format);
5043	int pass;
5044
5045	for (pass = 0; pass < 2; ++pass) {
5046	    int used = 0;
5047	    int first = 1;
5048	    int escaped = 0;
5049	    int squoted = 0;
5050	    int dquoted = 0;
5051	    int n;
5052
5053	    argc = 0;
5054	    for (n = 0; format[n] != '\0'; ++n) {
5055		if (escaped) {
5056		    blob[used++] = format[n];
5057		    escaped = 0;
5058		} else if (format[n] == '"') {
5059		    if (!squoted) {
5060			if (!dquoted)
5061			    blob[used++] = format[n];
5062			dquoted = !dquoted;
5063		    }
5064		} else if (format[n] == '\'') {
5065		    if (!dquoted) {
5066			if (!squoted)
5067			    blob[used++] = format[n];
5068			squoted = !squoted;
5069		    }
5070		} else if (format[n] == '\\') {
5071		    blob[used++] = format[n];
5072		    escaped = 1;
5073		} else {
5074		    if (first) {
5075			first = 0;
5076			if (pass) {
5077			    result[argc] = &blob[n];
5078			}
5079			++argc;
5080		    }
5081		    if (isspace((Char) format[n])) {
5082			first = !isspace((Char) format[n + 1]);
5083			if (squoted || dquoted) {
5084			    blob[used++] = format[n];
5085			} else if (first) {
5086			    blob[used++] = '\0';
5087			}
5088		    } else {
5089			blob[used++] = format[n];
5090		    }
5091		}
5092	    }
5093	    blob[used] = '\0';
5094	    assert(strlen(blob) <= strlen(format));
5095	    if (!pass) {
5096		result = TypeCallocN(char *, argc + 1);
5097		if (result == 0) {
5098		    free(blob);
5099		    break;
5100		}
5101	    }
5102	}
5103    }
5104#if OPT_TRACE
5105    if (result) {
5106	TRACE(("tokenizeFormat %s\n", format));
5107	for (argc = 0; result[argc]; ++argc) {
5108	    TRACE(("argv[%d] = %s\n", argc, result[argc]));
5109	}
5110    }
5111#endif
5112
5113    return result;
5114}
5115
5116static void
5117formatVideoAttrs(XtermWidget xw, char *buffer, CELL *cell)
5118{
5119    TScreen *screen = TScreenOf(xw);
5120    LineData *ld = GET_LINEDATA(screen, cell->row);
5121
5122    *buffer = '\0';
5123    if (ld != 0 && cell->col < (int) ld->lineSize) {
5124	IAttr attribs = ld->attribs[cell->col];
5125	const char *delim = "";
5126
5127	if (attribs & INVERSE) {
5128	    buffer += sprintf(buffer, "7");
5129	    delim = ";";
5130	}
5131	if (attribs & UNDERLINE) {
5132	    buffer += sprintf(buffer, "%s4", delim);
5133	    delim = ";";
5134	}
5135	if (attribs & BOLD) {
5136	    buffer += sprintf(buffer, "%s1", delim);
5137	    delim = ";";
5138	}
5139	if (attribs & BLINK) {
5140	    buffer += sprintf(buffer, "%s5", delim);
5141	    delim = ";";
5142	}
5143#if OPT_ISO_COLORS
5144	if (attribs & FG_COLOR) {
5145	    unsigned fg = extract_fg(xw, ld->color[cell->col], attribs);
5146	    if (fg < 8) {
5147		fg += 30;
5148	    } else if (fg < 16) {
5149		fg += 90;
5150	    } else {
5151		buffer += sprintf(buffer, "%s38;5", delim);
5152		delim = ";";
5153	    }
5154	    buffer += sprintf(buffer, "%s%u", delim, fg);
5155	    delim = ";";
5156	}
5157	if (attribs & BG_COLOR) {
5158	    unsigned bg = extract_bg(xw, ld->color[cell->col], attribs);
5159	    if (bg < 8) {
5160		bg += 40;
5161	    } else if (bg < 16) {
5162		bg += 100;
5163	    } else {
5164		buffer += sprintf(buffer, "%s48;5", delim);
5165		delim = ";";
5166	    }
5167	    (void) sprintf(buffer, "%s%u", delim, bg);
5168	}
5169#endif
5170    }
5171}
5172
5173static char *
5174formatStrlen(char *target, char *source, int freeit)
5175{
5176    if (source != 0) {
5177	sprintf(target, "%u", (unsigned) strlen(source));
5178	if (freeit) {
5179	    free(source);
5180	}
5181    } else {
5182	strcpy(target, "0");
5183    }
5184    return target;
5185}
5186
5187/* substitute data into format, reallocating the result */
5188static char *
5189expandFormat(XtermWidget xw,
5190	     const char *format,
5191	     char *data,
5192	     CELL *start,
5193	     CELL *finish)
5194{
5195    char *result = 0;
5196    if (!IsEmpty(format)) {
5197	static char empty[1];
5198	int pass;
5199	int n;
5200	char numbers[80];
5201
5202	if (data == 0)
5203	    data = empty;
5204
5205	for (pass = 0; pass < 2; ++pass) {
5206	    size_t need = 0;
5207
5208	    for (n = 0; format[n] != '\0'; ++n) {
5209
5210		if (format[n] == '%') {
5211		    char *value = 0;
5212
5213		    switch (format[++n]) {
5214		    case '%':
5215			if (pass) {
5216			    result[need] = format[n];
5217			}
5218			++need;
5219			break;
5220		    case 'P':
5221			sprintf(numbers, "%d;%d",
5222				TScreenOf(xw)->topline + start->row + 1,
5223				start->col + 1);
5224			value = numbers;
5225			break;
5226		    case 'p':
5227			sprintf(numbers, "%d;%d",
5228				TScreenOf(xw)->topline + finish->row + 1,
5229				finish->col + 1);
5230			value = numbers;
5231			break;
5232		    case 'R':
5233			value = formatStrlen(numbers, x_strrtrim(data), 1);
5234			break;
5235		    case 'r':
5236			value = x_strrtrim(data);
5237			break;
5238		    case 'S':
5239			value = formatStrlen(numbers, data, 0);
5240			break;
5241		    case 's':
5242			value = data;
5243			break;
5244		    case 'T':
5245			value = formatStrlen(numbers, x_strtrim(data), 1);
5246			break;
5247		    case 't':
5248			value = x_strtrim(data);
5249			break;
5250		    case 'V':
5251			formatVideoAttrs(xw, numbers, start);
5252			value = numbers;
5253			break;
5254		    case 'v':
5255			formatVideoAttrs(xw, numbers, finish);
5256			value = numbers;
5257			break;
5258		    default:
5259			if (pass) {
5260			    result[need] = format[n];
5261			}
5262			--n;
5263			++need;
5264			break;
5265		    }
5266		    if (value != 0) {
5267			if (pass) {
5268			    strcpy(result + need, value);
5269			}
5270			need += strlen(value);
5271			if (value != numbers && value != data) {
5272			    free(value);
5273			}
5274		    }
5275		} else {
5276		    if (pass) {
5277			result[need] = format[n];
5278		    }
5279		    ++need;
5280		}
5281	    }
5282	    if (pass) {
5283		result[need] = '\0';
5284	    } else {
5285		++need;
5286		result = malloc(need);
5287		if (result == 0) {
5288		    break;
5289		}
5290	    }
5291	}
5292    }
5293    TRACE(("expandFormat(%s) = %s\n", NonNull(format), NonNull(result)));
5294    return result;
5295}
5296
5297/* execute the command after forking.  The main process frees its data */
5298static void
5299executeCommand(pid_t pid, char **argv)
5300{
5301    (void) pid;
5302    if (argv != 0 && argv[0] != 0) {
5303	char *child_cwd = ProcGetCWD(pid);
5304
5305	if (fork() == 0) {
5306	    if (child_cwd) {
5307		IGNORE_RC(chdir(child_cwd));	/* We don't care if this fails */
5308	    }
5309	    execvp(argv[0], argv);
5310	    exit(EXIT_FAILURE);
5311	}
5312	free(child_cwd);
5313    }
5314}
5315
5316static void
5317freeArgv(char *blob, char **argv)
5318{
5319    if (blob) {
5320	free(blob);
5321	if (argv) {
5322	    int n;
5323	    for (n = 0; argv[n]; ++n)
5324		free(argv[n]);
5325	    free(argv);
5326	}
5327    }
5328}
5329
5330static void
5331reallyExecFormatted(Widget w, char *format, char *data, CELL *start, CELL *finish)
5332{
5333    XtermWidget xw;
5334
5335    if ((xw = getXtermWidget(w)) != 0) {
5336	char **argv;
5337
5338	if ((argv = tokenizeFormat(format)) != 0) {
5339	    char *blob = argv[0];
5340	    int argc;
5341
5342	    for (argc = 0; argv[argc] != 0; ++argc) {
5343		argv[argc] = expandFormat(xw, argv[argc], data, start, finish);
5344	    }
5345	    executeCommand(TScreenOf(xw)->pid, argv);
5346	    freeArgv(blob, argv);
5347	}
5348    }
5349}
5350
5351void
5352HandleExecFormatted(Widget w,
5353		    XEvent *event,
5354		    String *params,	/* selections */
5355		    Cardinal *num_params)
5356{
5357    XtermWidget xw;
5358
5359    TRACE(("HandleExecFormatted(%d)\n", *num_params));
5360    if ((xw = getXtermWidget(w)) != 0 &&
5361	(*num_params > 1)) {
5362	doSelectionFormat(xw, w, event, params, num_params, reallyExecFormatted);
5363    }
5364}
5365
5366void
5367HandleExecSelectable(Widget w,
5368		     XEvent *event,
5369		     String *params,	/* selections */
5370		     Cardinal *num_params)
5371{
5372    XtermWidget xw;
5373
5374    if ((xw = getXtermWidget(w)) != 0) {
5375	TRACE(("HandleExecSelectable(%d)\n", *num_params));
5376
5377	if (*num_params == 2) {
5378	    CELL start, finish;
5379	    char *data;
5380	    char **argv;
5381
5382	    data = getDataFromScreen(xw, event, params[1], &start, &finish);
5383	    if (data != 0) {
5384		if ((argv = tokenizeFormat(params[0])) != 0) {
5385		    char *blob = argv[0];
5386		    int argc;
5387
5388		    for (argc = 0; argv[argc] != 0; ++argc) {
5389			argv[argc] = expandFormat(xw, argv[argc], data,
5390						  &start, &finish);
5391		    }
5392		    executeCommand(TScreenOf(xw)->pid, argv);
5393		    freeArgv(blob, argv);
5394		}
5395		free(data);
5396	    }
5397	}
5398    }
5399}
5400
5401static void
5402reallyInsertFormatted(Widget w, char *format, char *data, CELL *start, CELL *finish)
5403{
5404    XtermWidget xw;
5405
5406    if ((xw = getXtermWidget(w)) != 0) {
5407	char *exps;
5408
5409	if ((exps = expandFormat(xw, format, data, start, finish)) != 0) {
5410	    unparseputs(xw, exps);
5411	    unparse_end(xw);
5412	    free(exps);
5413	}
5414    }
5415}
5416
5417void
5418HandleInsertFormatted(Widget w,
5419		      XEvent *event,
5420		      String *params,	/* selections */
5421		      Cardinal *num_params)
5422{
5423    XtermWidget xw;
5424
5425    TRACE(("HandleInsertFormatted(%d)\n", *num_params));
5426    if ((xw = getXtermWidget(w)) != 0 &&
5427	(*num_params > 1)) {
5428	doSelectionFormat(xw, w, event, params, num_params, reallyInsertFormatted);
5429    }
5430}
5431
5432void
5433HandleInsertSelectable(Widget w,
5434		       XEvent *event,
5435		       String *params,	/* selections */
5436		       Cardinal *num_params)
5437{
5438    XtermWidget xw;
5439
5440    if ((xw = getXtermWidget(w)) != 0) {
5441	TRACE(("HandleInsertSelectable(%d)\n", *num_params));
5442
5443	if (*num_params == 2) {
5444	    CELL start, finish;
5445	    char *data;
5446	    char *temp = x_strdup(params[0]);
5447
5448	    data = getDataFromScreen(xw, event, params[1], &start, &finish);
5449	    if (data != 0) {
5450		char *exps = expandFormat(xw, temp, data, &start, &finish);
5451		if (exps != 0) {
5452		    unparseputs(xw, exps);
5453		    unparse_end(xw);
5454		    free(exps);
5455		}
5456		free(data);
5457	    }
5458	    free(temp);
5459	}
5460    }
5461}
5462#endif /* OPT_SELECTION_OPS */
5463