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