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