button.c revision 01037d57
1/* $XTermId: button.c,v 1.492 2015/04/10 10:16:19 tom Exp $ */
2
3/*
4 * Copyright 1999-2014,2015 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 (type=%ld, length=%ld)\n", name, (long int) type, len));
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
1807    TRACE(("base64_flush count %d, pad %d (%d)\n",
1808	   screen->base64_count,
1809	   screen->base64_pad,
1810	   screen->base64_pad & 3));
1811
1812    switch (screen->base64_count) {
1813    case 0:
1814	break;
1815    case 2:
1816	x = CharOf(base64_code[screen->base64_accu << 4]);
1817	tty_vwrite(screen->respond, &x, 1);
1818	break;
1819    case 4:
1820	x = CharOf(base64_code[screen->base64_accu << 2]);
1821	tty_vwrite(screen->respond, &x, 1);
1822	break;
1823    }
1824    if (screen->base64_pad & 3) {
1825	tty_vwrite(screen->respond,
1826		   (const Char *) "===",
1827		   (unsigned) (3 - (screen->base64_pad & 3)));
1828    }
1829    screen->base64_count = 0;
1830    screen->base64_accu = 0;
1831    screen->base64_pad = 0;
1832}
1833#endif /* OPT_PASTE64 */
1834
1835/*
1836 * Translate ISO-8859-1 or UTF-8 data to NRCS.
1837 */
1838static void
1839ToNational(TScreen *screen, Char *buffer, unsigned *length)
1840{
1841    int gsetL = screen->gsets[screen->curgl];
1842    int gsetR = screen->gsets[screen->curgr];
1843    unsigned chr, out, gl, gr;
1844    Char *p;
1845
1846#if OPT_WIDE_CHARS
1847    if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
1848	PtyData *data = TypeXtMallocX(PtyData, *length);
1849
1850	memset(data, 0, sizeof(*data));
1851	data->next = data->buffer;
1852	data->last = data->buffer + *length;
1853	memcpy(data->buffer, buffer, (size_t) *length);
1854	p = buffer;
1855	while (data->next < data->last) {
1856	    if (!decodeUtf8(screen, data)) {
1857		data->utf_size = 1;
1858		data->utf_data = data->next[0];
1859	    }
1860	    data->next += data->utf_size;
1861	    chr = data->utf_data;
1862	    out = chr;
1863	    if ((gl = xtermCharSetIn(screen, chr, gsetL)) != chr) {
1864		out = gl;
1865	    } else if ((gr = xtermCharSetIn(screen, chr, gsetR)) != chr) {
1866		out = gr;
1867	    }
1868	    *p++ = (Char) ((out < 256) ? out : ' ');
1869	}
1870	*length = (unsigned) (p - buffer);
1871	free(data);
1872    } else
1873#endif
1874    {
1875	for (p = buffer; (int) (p - buffer) < (int) *length; ++p) {
1876	    chr = *p;
1877	    out = chr;
1878	    if ((gl = xtermCharSetIn(screen, chr, gsetL)) != chr) {
1879		out = gl;
1880	    } else if ((gr = xtermCharSetIn(screen, chr, gsetR)) != chr) {
1881		out = gr;
1882	    }
1883	    *p = (Char) out;
1884	}
1885    }
1886}
1887
1888static void
1889_qWriteSelectionData(XtermWidget xw, Char *lag, unsigned length)
1890{
1891    TScreen *screen = TScreenOf(xw);
1892
1893    /*
1894     * If we are pasting into a window which is using NRCS, we want to map
1895     * the text from the normal encoding (ISO-8859-1 or UTF-8) into the coding
1896     * that an application would use to write characters with NRCS.
1897     *
1898     * TODO: handle conversion from UTF-8, and adjust length.  This can be done
1899     * in the same buffer because the target is always 8-bit.
1900     */
1901    if ((xw->flags & NATIONAL) && (length != 0)) {
1902	ToNational(screen, lag, &length);
1903    }
1904#if OPT_PASTE64
1905    if (screen->base64_paste) {
1906	/* Send data as base64 */
1907	Char *p = lag;
1908	Char buf[64];
1909	unsigned x = 0;
1910
1911	TRACE(("convert to base64 %d:%s\n", length, visibleChars(p, length)));
1912
1913	/*
1914	 * Handle the case where the selection is from _this_ xterm, which
1915	 * puts part of the reply in the buffer before the selection callback
1916	 * happens.
1917	 */
1918	if (screen->base64_paste && screen->unparse_len) {
1919	    unparse_end(xw);
1920	}
1921	while (length--) {
1922	    switch (screen->base64_count) {
1923	    case 0:
1924		buf[x++] = CharOf(base64_code[*p >> 2]);
1925		screen->base64_accu = (unsigned) (*p & 0x3);
1926		screen->base64_count = 2;
1927		++p;
1928		break;
1929	    case 2:
1930		buf[x++] = CharOf(base64_code[(screen->base64_accu << 4) +
1931					      (*p >> 4)]);
1932		screen->base64_accu = (unsigned) (*p & 0xF);
1933		screen->base64_count = 4;
1934		++p;
1935		break;
1936	    case 4:
1937		buf[x++] = CharOf(base64_code[(screen->base64_accu << 2) +
1938					      (*p >> 6)]);
1939		buf[x++] = CharOf(base64_code[*p & 0x3F]);
1940		screen->base64_accu = 0;
1941		screen->base64_count = 0;
1942		++p;
1943		break;
1944	    }
1945	    if (x >= 63) {
1946		/* Write 63 or 64 characters */
1947		screen->base64_pad += x;
1948		TRACE(("writing base64 interim %s\n", visibleChars(buf, x)));
1949		tty_vwrite(screen->respond, buf, x);
1950		x = 0;
1951	    }
1952	}
1953	if (x != 0) {
1954	    screen->base64_pad += x;
1955	    TRACE(("writing base64 finish %s\n", visibleChars(buf, x)));
1956	    tty_vwrite(screen->respond, buf, x);
1957	}
1958    } else
1959#endif /* OPT_PASTE64 */
1960#if OPT_READLINE
1961    if (SCREEN_FLAG(screen, paste_quotes)) {
1962	while (length--) {
1963	    tty_vwrite(screen->respond, (const Char *) "\026", 1);	/* Control-V */
1964	    tty_vwrite(screen->respond, lag++, 1);
1965	}
1966    } else
1967#endif
1968    {
1969	TRACE(("writing base64 padding %s\n", visibleChars(lag, length)));
1970	tty_vwrite(screen->respond, lag, length);
1971    }
1972}
1973
1974static void
1975_WriteSelectionData(XtermWidget xw, Char *line, size_t length)
1976{
1977    /* Write data to pty a line at a time. */
1978    /* Doing this one line at a time may no longer be necessary
1979       because v_write has been re-written. */
1980
1981    TScreen *screen = TScreenOf(xw);
1982    Char *lag, *end;
1983
1984    /* in the VMS version, if tt_pasting isn't set to True then qio
1985       reads aren't blocked and an infinite loop is entered, where the
1986       pasted text shows up as new input, goes in again, shows up
1987       again, ad nauseum. */
1988#ifdef VMS
1989    tt_pasting = True;
1990#endif
1991
1992    end = &line[length];
1993    lag = line;
1994
1995#if OPT_PASTE64
1996    if (screen->base64_paste) {
1997	_qWriteSelectionData(xw, lag, (unsigned) (end - lag));
1998	base64_flush(screen);
1999    } else
2000#endif
2001    {
2002	if (!SCREEN_FLAG(screen, paste_literal_nl)) {
2003	    Char *cp;
2004	    for (cp = line; cp != end; cp++) {
2005		if (*cp == '\n') {
2006		    *cp = '\r';
2007		    _qWriteSelectionData(xw, lag, (unsigned) (cp - lag + 1));
2008		    lag = cp + 1;
2009		}
2010	    }
2011	}
2012
2013	if (lag != end) {
2014	    _qWriteSelectionData(xw, lag, (unsigned) (end - lag));
2015	}
2016    }
2017#ifdef VMS
2018    tt_pasting = False;
2019    tt_start_read();		/* reenable reads or a character may be lost */
2020#endif
2021}
2022
2023#if OPT_READLINE
2024static void
2025_WriteKey(TScreen *screen, const Char *in)
2026{
2027    Char line[16];
2028    unsigned count = 0;
2029    size_t length = strlen((const char *) in);
2030
2031    if (screen->control_eight_bits) {
2032	line[count++] = ANSI_CSI;
2033    } else {
2034	line[count++] = ANSI_ESC;
2035	line[count++] = '[';
2036    }
2037    while (length--)
2038	line[count++] = *in++;
2039    line[count++] = '~';
2040    tty_vwrite(screen->respond, line, count);
2041}
2042#endif /* OPT_READLINE */
2043
2044/*
2045 * Unless enabled by the user, strip control characters other than formatting.
2046 */
2047static size_t
2048removeControls(XtermWidget xw, char *value)
2049{
2050    TScreen *screen = TScreenOf(xw);
2051    size_t dst = 0;
2052    size_t src = 0;
2053
2054    if (screen->allowPasteControls) {
2055	dst = strlen(value);
2056    } else {
2057	while ((value[dst] = value[src]) != '\0') {
2058	    int ch = CharOf(value[src++]);
2059	    if (ch < 32) {
2060		switch (ch) {
2061		case '\b':
2062		case '\t':
2063		case '\n':
2064		case '\r':
2065		    ++dst;
2066		    break;
2067		default:
2068		    continue;
2069		}
2070	    }
2071#if OPT_WIDE_CHARS
2072	    else if (screen->utf8_inparse || screen->utf8_nrc_mode)
2073		++dst;
2074#endif
2075#if OPT_C1_PRINT || OPT_WIDE_CHARS
2076	    else if (screen->c1_printable)
2077		++dst;
2078#endif
2079	    else if (ch >= 128 && ch < 160)
2080		continue;
2081	    else
2082		++dst;
2083	}
2084    }
2085    return dst;
2086}
2087
2088/* SelectionReceived: stuff received selection text into pty */
2089
2090/* ARGSUSED */
2091static void
2092SelectionReceived(Widget w,
2093		  XtPointer client_data,
2094		  Atom *selection GCC_UNUSED,
2095		  Atom *type,
2096		  XtPointer value,
2097		  unsigned long *length,
2098		  int *format)
2099{
2100    char **text_list = NULL;
2101    int text_list_count;
2102    XTextProperty text_prop;
2103    TScreen *screen;
2104    Display *dpy;
2105#if OPT_TRACE && OPT_WIDE_CHARS
2106    Char *line = (Char *) value;
2107#endif
2108
2109    XtermWidget xw;
2110
2111    if ((xw = getXtermWidget(w)) == 0)
2112	return;
2113
2114    screen = TScreenOf(xw);
2115    dpy = XtDisplay(w);
2116
2117    if (*type == 0		/*XT_CONVERT_FAIL */
2118	|| *length == 0
2119	|| value == NULL) {
2120	TRACE(("...no data to convert\n"));
2121	goto fail;
2122    }
2123
2124    text_prop.value = (unsigned char *) value;
2125    text_prop.encoding = *type;
2126    text_prop.format = *format;
2127    text_prop.nitems = *length;
2128
2129    TRACE(("SelectionReceived %s %s format %d, nitems %ld\n",
2130	   XGetAtomName(screen->display, *selection),
2131	   visibleSelectionTarget(dpy, text_prop.encoding),
2132	   text_prop.format,
2133	   text_prop.nitems));
2134
2135#if OPT_WIDE_CHARS
2136    if (XSupportsLocale() && screen->wide_chars) {
2137	if (*type == XA_UTF8_STRING(dpy) ||
2138	    *type == XA_STRING ||
2139	    *type == XA_COMPOUND_TEXT(dpy)) {
2140	    GettingSelection(dpy, *type, line, *length);
2141	    if (Xutf8TextPropertyToTextList(dpy, &text_prop,
2142					    &text_list,
2143					    &text_list_count) < 0) {
2144		TRACE(("default Xutf8 Conversion failed\n"));
2145		text_list = NULL;
2146	    }
2147	}
2148    } else
2149#endif /* OPT_WIDE_CHARS */
2150    {
2151	/* Convert the selection to locale's multibyte encoding. */
2152
2153	if (*type == XA_UTF8_STRING(dpy) ||
2154	    *type == XA_STRING ||
2155	    *type == XA_COMPOUND_TEXT(dpy)) {
2156	    Status rc;
2157
2158	    GettingSelection(dpy, *type, line, *length);
2159
2160#if OPT_WIDE_CHARS
2161	    if (*type == XA_UTF8_STRING(dpy) &&
2162		!(screen->wide_chars || screen->c1_printable)) {
2163		rc = xtermUtf8ToTextList(xw, &text_prop,
2164					 &text_list, &text_list_count);
2165	    } else
2166#endif
2167	    if (*type == XA_STRING && (!XSupportsLocale() || screen->brokenSelections)) {
2168		rc = XTextPropertyToStringList(&text_prop,
2169					       &text_list, &text_list_count);
2170	    } else {
2171		rc = XmbTextPropertyToTextList(dpy, &text_prop,
2172					       &text_list,
2173					       &text_list_count);
2174	    }
2175	    if (rc < 0) {
2176		TRACE(("Conversion failed\n"));
2177		text_list = NULL;
2178	    }
2179	}
2180    }
2181
2182    if (text_list != NULL && text_list_count != 0) {
2183	int i;
2184
2185#if OPT_PASTE64
2186	if (screen->base64_paste) {
2187	    /* EMPTY */ ;
2188	} else
2189#endif
2190#if OPT_READLINE
2191	if (SCREEN_FLAG(screen, paste_brackets)) {
2192	    _WriteKey(screen, (const Char *) "200");
2193	}
2194#endif
2195	for (i = 0; i < text_list_count; i++) {
2196	    size_t len = removeControls(xw, text_list[i]);
2197
2198	    if (screen->selectToBuffer) {
2199		InternalSelect *mydata = &(screen->internal_select);
2200		size_t have = (mydata->buffer
2201			       ? strlen(mydata->buffer)
2202			       : 0);
2203		size_t need = have + len + 1;
2204		char *buffer = realloc(mydata->buffer, need);
2205
2206		screen->selectToBuffer = False;
2207#if OPT_PASTE64
2208		screen->base64_paste = mydata->base64_paste;
2209#endif
2210#if OPT_READLINE
2211		screen->paste_brackets = mydata->paste_brackets;
2212#endif
2213		if (buffer != 0) {
2214		    strcpy(buffer + have, text_list[i]);
2215		    mydata->buffer = buffer;
2216		}
2217		TRACE(("FormatSelect %d.%d .. %d.%d %s\n",
2218		       screen->startSel.row,
2219		       screen->startSel.col,
2220		       screen->endSel.row,
2221		       screen->endSel.col,
2222		       mydata->buffer));
2223		mydata->format_select(w, mydata->format, mydata->buffer,
2224				      &(screen->startSel),
2225				      &(screen->endSel));
2226
2227		free(mydata->format);
2228		free(mydata->buffer);
2229		memset(mydata, 0, sizeof(*mydata));
2230	    } else {
2231		_WriteSelectionData(xw, (Char *) text_list[i], len);
2232	    }
2233	}
2234#if OPT_PASTE64
2235	if (screen->base64_paste) {
2236	    FinishPaste64(xw);
2237	} else
2238#endif
2239#if OPT_READLINE
2240	if (SCREEN_FLAG(screen, paste_brackets)) {
2241	    _WriteKey(screen, (const Char *) "201");
2242	}
2243#endif
2244	XFreeStringList(text_list);
2245    } else {
2246	TRACE(("...empty text-list\n"));
2247	goto fail;
2248    }
2249
2250    XtFree((char *) client_data);
2251    XtFree((char *) value);
2252
2253    return;
2254
2255  fail:
2256    if (client_data != 0) {
2257	struct _SelectionList *list = (struct _SelectionList *) client_data;
2258
2259	TRACE(("SelectionReceived ->xtermGetSelection\n"));
2260	xtermGetSelection(w, list->time,
2261			  list->params, list->count, list->targets);
2262	XtFree((char *) client_data);
2263#if OPT_PASTE64
2264    } else {
2265	FinishPaste64(xw);
2266#endif
2267    }
2268    return;
2269}
2270
2271void
2272HandleInsertSelection(Widget w,
2273		      XEvent *event,	/* assumed to be XButtonEvent* */
2274		      String *params,	/* selections in precedence order */
2275		      Cardinal *num_params)
2276{
2277    XtermWidget xw;
2278
2279    if ((xw = getXtermWidget(w)) != 0) {
2280	TRACE(("HandleInsertSelection\n"));
2281	if (!SendMousePosition(xw, event)) {
2282#if OPT_READLINE
2283	    int ldelta;
2284	    TScreen *screen = TScreenOf(xw);
2285	    if (IsBtnEvent(event)
2286	    /* Disable on Shift-mouse, including the application-mouse modes */
2287		&& !(KeyModifiers(event) & ShiftMask)
2288		&& (screen->send_mouse_pos == MOUSE_OFF)
2289		&& SCREEN_FLAG(screen, paste_moves)
2290		&& rowOnCurrentLine(screen, eventRow(screen, event), &ldelta))
2291		ReadLineMovePoint(screen, eventColBetween(screen, event), ldelta);
2292#endif /* OPT_READLINE */
2293
2294	    xtermGetSelection(w, event->xbutton.time, params, *num_params, NULL);
2295	}
2296    }
2297}
2298
2299static SelectUnit
2300EvalSelectUnit(XtermWidget xw,
2301	       Time buttonDownTime,
2302	       SelectUnit defaultUnit,
2303	       unsigned int button)
2304{
2305    TScreen *screen = TScreenOf(xw);
2306    SelectUnit result;
2307    int delta;
2308
2309    if (button != screen->lastButton) {
2310	delta = screen->multiClickTime + 1;
2311    } else if (screen->lastButtonUpTime == (Time) 0) {
2312	/* first time and once in a blue moon */
2313	delta = screen->multiClickTime + 1;
2314    } else if (buttonDownTime > screen->lastButtonUpTime) {
2315	/* most of the time */
2316	delta = (int) (buttonDownTime - screen->lastButtonUpTime);
2317    } else {
2318	/* time has rolled over since lastButtonUpTime */
2319	delta = (int) ((((Time) ~ 0) - screen->lastButtonUpTime) + buttonDownTime);
2320    }
2321
2322    if (delta > screen->multiClickTime) {
2323	screen->numberOfClicks = 1;
2324	result = defaultUnit;
2325    } else {
2326	result = screen->selectMap[screen->numberOfClicks % screen->maxClicks];
2327	screen->numberOfClicks += 1;
2328    }
2329    TRACE(("EvalSelectUnit(%d) = %d\n", screen->numberOfClicks, result));
2330    return result;
2331}
2332
2333static void
2334do_select_start(XtermWidget xw,
2335		XEvent *event,	/* must be XButtonEvent* */
2336		CELL *cell)
2337{
2338    TScreen *screen = TScreenOf(xw);
2339
2340    if (SendMousePosition(xw, event))
2341	return;
2342    screen->selectUnit = EvalSelectUnit(xw,
2343					event->xbutton.time,
2344					Select_CHAR,
2345					event->xbutton.button);
2346    screen->replyToEmacs = False;
2347
2348#if OPT_READLINE
2349    lastButtonDownTime = event->xbutton.time;
2350#endif
2351
2352    StartSelect(xw, cell);
2353}
2354
2355/* ARGSUSED */
2356void
2357HandleSelectStart(Widget w,
2358		  XEvent *event,	/* must be XButtonEvent* */
2359		  String *params GCC_UNUSED,
2360		  Cardinal *num_params GCC_UNUSED)
2361{
2362    XtermWidget xw;
2363
2364    if ((xw = getXtermWidget(w)) != 0) {
2365	TScreen *screen = TScreenOf(xw);
2366	CELL cell;
2367
2368	TRACE(("HandleSelectStart\n"));
2369	screen->firstValidRow = 0;
2370	screen->lastValidRow = screen->max_row;
2371	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
2372
2373#if OPT_READLINE
2374	ExtendingSelection = 0;
2375#endif
2376
2377	do_select_start(xw, event, &cell);
2378    }
2379}
2380
2381/* ARGSUSED */
2382void
2383HandleKeyboardSelectStart(Widget w,
2384			  XEvent *event,	/* must be XButtonEvent* */
2385			  String *params GCC_UNUSED,
2386			  Cardinal *num_params GCC_UNUSED)
2387{
2388    XtermWidget xw;
2389
2390    if ((xw = getXtermWidget(w)) != 0) {
2391	TScreen *screen = TScreenOf(xw);
2392
2393	TRACE(("HandleKeyboardSelectStart\n"));
2394	do_select_start(xw, event, &screen->cursorp);
2395    }
2396}
2397
2398static void
2399TrackDown(XtermWidget xw, XButtonEvent *event)
2400{
2401    TScreen *screen = TScreenOf(xw);
2402    CELL cell;
2403
2404    screen->selectUnit = EvalSelectUnit(xw,
2405					event->time,
2406					Select_CHAR,
2407					event->button);
2408    if (screen->numberOfClicks > 1) {
2409	PointToCELL(screen, event->y, event->x, &cell);
2410	screen->replyToEmacs = True;
2411	StartSelect(xw, &cell);
2412    } else {
2413	screen->waitingForTrackInfo = True;
2414	EditorButton(xw, event);
2415    }
2416}
2417
2418#define boundsCheck(x)	if (x < 0) \
2419			    x = 0; \
2420			else if (x >= screen->max_row) \
2421			    x = screen->max_row
2422
2423void
2424TrackMouse(XtermWidget xw,
2425	   int func,
2426	   CELL *start,
2427	   int firstrow,
2428	   int lastrow)
2429{
2430    TScreen *screen = TScreenOf(xw);
2431
2432    if (screen->waitingForTrackInfo) {	/* if Timed, ignore */
2433	screen->waitingForTrackInfo = False;
2434
2435	if (func != 0) {
2436	    CELL first = *start;
2437
2438	    boundsCheck(first.row);
2439	    boundsCheck(firstrow);
2440	    boundsCheck(lastrow);
2441	    screen->firstValidRow = firstrow;
2442	    screen->lastValidRow = lastrow;
2443	    screen->replyToEmacs = True;
2444	    StartSelect(xw, &first);
2445	}
2446    }
2447}
2448
2449static void
2450StartSelect(XtermWidget xw, const CELL *cell)
2451{
2452    TScreen *screen = TScreenOf(xw);
2453
2454    TRACE(("StartSelect row=%d, col=%d\n", cell->row, cell->col));
2455    if (screen->cursor_state)
2456	HideCursor();
2457    if (screen->numberOfClicks == 1) {
2458	/* set start of selection */
2459	screen->rawPos = *cell;
2460    }
2461    /* else use old values in rawPos */
2462    screen->saveStartR = screen->startExt = screen->rawPos;
2463    screen->saveEndR = screen->endExt = screen->rawPos;
2464    if (Coordinate(screen, cell) < Coordinate(screen, &(screen->rawPos))) {
2465	screen->eventMode = LEFTEXTENSION;
2466	screen->startExt = *cell;
2467    } else {
2468	screen->eventMode = RIGHTEXTENSION;
2469	screen->endExt = *cell;
2470    }
2471    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False);
2472}
2473
2474static void
2475EndExtend(XtermWidget xw,
2476	  XEvent *event,	/* must be XButtonEvent */
2477	  String *params,	/* selections */
2478	  Cardinal num_params,
2479	  Bool use_cursor_loc)
2480{
2481    CELL cell;
2482    unsigned count;
2483    TScreen *screen = TScreenOf(xw);
2484    Char line[64];
2485
2486    if (use_cursor_loc) {
2487	cell = screen->cursorp;
2488    } else {
2489	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
2490    }
2491    ExtendExtend(xw, &cell);
2492    screen->lastButtonUpTime = event->xbutton.time;
2493    screen->lastButton = event->xbutton.button;
2494    if (!isSameCELL(&(screen->startSel), &(screen->endSel))) {
2495	if (screen->replyToEmacs) {
2496	    count = 0;
2497	    if (screen->control_eight_bits) {
2498		line[count++] = ANSI_CSI;
2499	    } else {
2500		line[count++] = ANSI_ESC;
2501		line[count++] = '[';
2502	    }
2503	    if (isSameCELL(&(screen->rawPos), &(screen->startSel))
2504		&& isSameCELL(&cell, &(screen->endSel))) {
2505		/* Use short-form emacs select */
2506
2507		switch (screen->extend_coords) {
2508		case 0:
2509		case SET_EXT_MODE_MOUSE:
2510		    line[count++] = 't';
2511		    break;
2512		case SET_SGR_EXT_MODE_MOUSE:
2513		    line[count++] = '<';
2514		    break;
2515		}
2516
2517		count = EmitMousePosition(screen, line, count, screen->endSel.col);
2518		count = EmitMousePositionSeparator(screen, line, count);
2519		count = EmitMousePosition(screen, line, count, screen->endSel.row);
2520
2521		switch (screen->extend_coords) {
2522		case SET_SGR_EXT_MODE_MOUSE:
2523		case SET_URXVT_EXT_MODE_MOUSE:
2524		    line[count++] = 't';
2525		    break;
2526		}
2527	    } else {
2528		/* long-form, specify everything */
2529
2530		switch (screen->extend_coords) {
2531		case 0:
2532		case SET_EXT_MODE_MOUSE:
2533		    line[count++] = 'T';
2534		    break;
2535		case SET_SGR_EXT_MODE_MOUSE:
2536		    line[count++] = '<';
2537		    break;
2538		}
2539
2540		count = EmitMousePosition(screen, line, count, screen->startSel.col);
2541		count = EmitMousePositionSeparator(screen, line, count);
2542		count = EmitMousePosition(screen, line, count, screen->startSel.row);
2543		count = EmitMousePositionSeparator(screen, line, count);
2544		count = EmitMousePosition(screen, line, count, screen->endSel.col);
2545		count = EmitMousePositionSeparator(screen, line, count);
2546		count = EmitMousePosition(screen, line, count, screen->endSel.row);
2547		count = EmitMousePositionSeparator(screen, line, count);
2548		count = EmitMousePosition(screen, line, count, cell.col);
2549		count = EmitMousePositionSeparator(screen, line, count);
2550		count = EmitMousePosition(screen, line, count, cell.row);
2551
2552		switch (screen->extend_coords) {
2553		case SET_SGR_EXT_MODE_MOUSE:
2554		case SET_URXVT_EXT_MODE_MOUSE:
2555		    line[count++] = 'T';
2556		    break;
2557		}
2558	    }
2559	    v_write(screen->respond, line, count);
2560	    TrackText(xw, &zeroCELL, &zeroCELL);
2561	}
2562    }
2563    SelectSet(xw, event, params, num_params);
2564    screen->eventMode = NORMAL;
2565}
2566
2567void
2568HandleSelectSet(Widget w,
2569		XEvent *event,
2570		String *params,
2571		Cardinal *num_params)
2572{
2573    XtermWidget xw;
2574
2575    if ((xw = getXtermWidget(w)) != 0) {
2576	TRACE(("HandleSelectSet\n"));
2577	SelectSet(xw, event, params, *num_params);
2578    }
2579}
2580
2581/* ARGSUSED */
2582static void
2583SelectSet(XtermWidget xw,
2584	  XEvent *event GCC_UNUSED,
2585	  String *params,
2586	  Cardinal num_params)
2587{
2588    TScreen *screen = TScreenOf(xw);
2589
2590    TRACE(("SelectSet\n"));
2591    /* Only do select stuff if non-null select */
2592    if (!isSameCELL(&(screen->startSel), &(screen->endSel))) {
2593	SaltTextAway(xw, &(screen->startSel), &(screen->endSel));
2594	_OwnSelection(xw, params, num_params);
2595    } else {
2596	ScrnDisownSelection(xw);
2597    }
2598}
2599
2600#define Abs(x)		((x) < 0 ? -(x) : (x))
2601
2602/* ARGSUSED */
2603static void
2604do_start_extend(XtermWidget xw,
2605		XEvent *event,	/* must be XButtonEvent* */
2606		String *params GCC_UNUSED,
2607		Cardinal *num_params GCC_UNUSED,
2608		Bool use_cursor_loc)
2609{
2610    TScreen *screen = TScreenOf(xw);
2611    int coord;
2612    CELL cell;
2613
2614    if (SendMousePosition(xw, event))
2615	return;
2616
2617    screen->firstValidRow = 0;
2618    screen->lastValidRow = screen->max_row;
2619#if OPT_READLINE
2620    if ((KeyModifiers(event) & ShiftMask)
2621	|| event->xbutton.button != Button3
2622	|| !(SCREEN_FLAG(screen, dclick3_deletes)))
2623#endif
2624	screen->selectUnit = EvalSelectUnit(xw,
2625					    event->xbutton.time,
2626					    screen->selectUnit,
2627					    event->xbutton.button);
2628    screen->replyToEmacs = False;
2629
2630#if OPT_READLINE
2631    CheckSecondPress3(screen, event);
2632#endif
2633
2634    if (screen->numberOfClicks == 1
2635	|| (SCREEN_FLAG(screen, dclick3_deletes)	/* Dclick special */
2636	    &&!(KeyModifiers(event) & ShiftMask))) {
2637	/* Save existing selection so we can reestablish it if the guy
2638	   extends past the other end of the selection */
2639	screen->saveStartR = screen->startExt = screen->startRaw;
2640	screen->saveEndR = screen->endExt = screen->endRaw;
2641    } else {
2642	/* He just needed the selection mode changed, use old values. */
2643	screen->startExt = screen->startRaw = screen->saveStartR;
2644	screen->endExt = screen->endRaw = screen->saveEndR;
2645    }
2646    if (use_cursor_loc) {
2647	cell = screen->cursorp;
2648    } else {
2649	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
2650    }
2651    coord = Coordinate(screen, &cell);
2652
2653    if (Abs(coord - Coordinate(screen, &(screen->startSel)))
2654	< Abs(coord - Coordinate(screen, &(screen->endSel)))
2655	|| coord < Coordinate(screen, &(screen->startSel))) {
2656	/* point is close to left side of selection */
2657	screen->eventMode = LEFTEXTENSION;
2658	screen->startExt = cell;
2659    } else {
2660	/* point is close to left side of selection */
2661	screen->eventMode = RIGHTEXTENSION;
2662	screen->endExt = cell;
2663    }
2664    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), True);
2665
2666#if OPT_READLINE
2667    if (!isSameCELL(&(screen->startSel), &(screen->endSel)))
2668	ExtendingSelection = 1;
2669#endif
2670}
2671
2672static void
2673ExtendExtend(XtermWidget xw, const CELL *cell)
2674{
2675    TScreen *screen = TScreenOf(xw);
2676    int coord = Coordinate(screen, cell);
2677
2678    TRACE(("ExtendExtend row=%d, col=%d\n", cell->row, cell->col));
2679    if (screen->eventMode == LEFTEXTENSION
2680	&& ((coord + (screen->selectUnit != Select_CHAR))
2681	    > Coordinate(screen, &(screen->endSel)))) {
2682	/* Whoops, he's changed his mind.  Do RIGHTEXTENSION */
2683	screen->eventMode = RIGHTEXTENSION;
2684	screen->startExt = screen->saveStartR;
2685    } else if (screen->eventMode == RIGHTEXTENSION
2686	       && coord < Coordinate(screen, &(screen->startSel))) {
2687	/* Whoops, he's changed his mind.  Do LEFTEXTENSION */
2688	screen->eventMode = LEFTEXTENSION;
2689	screen->endExt = screen->saveEndR;
2690    }
2691    if (screen->eventMode == LEFTEXTENSION) {
2692	screen->startExt = *cell;
2693    } else {
2694	screen->endExt = *cell;
2695    }
2696    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False);
2697
2698#if OPT_READLINE
2699    if (!isSameCELL(&(screen->startSel), &(screen->endSel)))
2700	ExtendingSelection = 1;
2701#endif
2702}
2703
2704void
2705HandleStartExtend(Widget w,
2706		  XEvent *event,	/* must be XButtonEvent* */
2707		  String *params,	/* unused */
2708		  Cardinal *num_params)		/* unused */
2709{
2710    XtermWidget xw;
2711
2712    if ((xw = getXtermWidget(w)) != 0) {
2713	TRACE(("HandleStartExtend\n"));
2714	do_start_extend(xw, event, params, num_params, False);
2715    }
2716}
2717
2718void
2719HandleKeyboardStartExtend(Widget w,
2720			  XEvent *event,	/* must be XButtonEvent* */
2721			  String *params,	/* unused */
2722			  Cardinal *num_params)		/* unused */
2723{
2724    XtermWidget xw;
2725
2726    if ((xw = getXtermWidget(w)) != 0) {
2727	TRACE(("HandleKeyboardStartExtend\n"));
2728	do_start_extend(xw, event, params, num_params, True);
2729    }
2730}
2731
2732void
2733ScrollSelection(TScreen *screen, int amount, Bool always)
2734{
2735    int minrow = INX2ROW(screen, -screen->savedlines);
2736    int maxrow = INX2ROW(screen, screen->max_row);
2737    int maxcol = screen->max_col;
2738
2739#define scroll_update_one(cell) \
2740	(cell)->row += amount; \
2741	if ((cell)->row < minrow) { \
2742	    (cell)->row = minrow; \
2743	    (cell)->col = 0; \
2744	} \
2745	if ((cell)->row > maxrow) { \
2746	    (cell)->row = maxrow; \
2747	    (cell)->col = maxcol; \
2748	}
2749
2750    scroll_update_one(&(screen->startRaw));
2751    scroll_update_one(&(screen->endRaw));
2752    scroll_update_one(&(screen->startSel));
2753    scroll_update_one(&(screen->endSel));
2754
2755    scroll_update_one(&(screen->rawPos));
2756
2757    /*
2758     * If we are told to scroll the selection but it lies outside the scrolling
2759     * margins, then that could cause the selection to move (bad).  It is not
2760     * simple to fix, because this function is called both for the scrollbar
2761     * actions as well as application scrolling.  The 'always' flag is set in
2762     * the former case.  The rest of the logic handles the latter.
2763     */
2764    if (ScrnHaveSelection(screen)) {
2765	int adjust;
2766
2767	adjust = ROW2INX(screen, screen->startH.row);
2768	if (always
2769	    || !ScrnHaveRowMargins(screen)
2770	    || ScrnIsRowInMargins(screen, adjust)) {
2771	    scroll_update_one(&screen->startH);
2772	}
2773	adjust = ROW2INX(screen, screen->endH.row);
2774	if (always
2775	    || !ScrnHaveRowMargins(screen)
2776	    || ScrnIsRowInMargins(screen, adjust)) {
2777	    scroll_update_one(&screen->endH);
2778	}
2779    }
2780
2781    screen->startHCoord = Coordinate(screen, &screen->startH);
2782    screen->endHCoord = Coordinate(screen, &screen->endH);
2783}
2784
2785/*ARGSUSED*/
2786void
2787ResizeSelection(TScreen *screen GCC_UNUSED, int rows, int cols)
2788{
2789    rows--;			/* decr to get 0-max */
2790    cols--;
2791
2792    if (screen->startRaw.row > rows)
2793	screen->startRaw.row = rows;
2794    if (screen->startSel.row > rows)
2795	screen->startSel.row = rows;
2796    if (screen->endRaw.row > rows)
2797	screen->endRaw.row = rows;
2798    if (screen->endSel.row > rows)
2799	screen->endSel.row = rows;
2800    if (screen->rawPos.row > rows)
2801	screen->rawPos.row = rows;
2802
2803    if (screen->startRaw.col > cols)
2804	screen->startRaw.col = cols;
2805    if (screen->startSel.col > cols)
2806	screen->startSel.col = cols;
2807    if (screen->endRaw.col > cols)
2808	screen->endRaw.col = cols;
2809    if (screen->endSel.col > cols)
2810	screen->endSel.col = cols;
2811    if (screen->rawPos.col > cols)
2812	screen->rawPos.col = cols;
2813}
2814
2815#if OPT_WIDE_CHARS
2816Bool
2817iswide(int i)
2818{
2819    return (i == HIDDEN_CHAR) || (WideCells(i) == 2);
2820}
2821
2822#define isWideCell(row, col) iswide((int)XTERM_CELL(row, col))
2823#endif
2824
2825static void
2826PointToCELL(TScreen *screen,
2827	    int y,
2828	    int x,
2829	    CELL *cell)
2830/* Convert pixel coordinates to character coordinates.
2831   Rows are clipped between firstValidRow and lastValidRow.
2832   Columns are clipped between to be 0 or greater, but are not clipped to some
2833       maximum value. */
2834{
2835    cell->row = (y - screen->border) / FontHeight(screen);
2836    if (cell->row < screen->firstValidRow)
2837	cell->row = screen->firstValidRow;
2838    else if (cell->row > screen->lastValidRow)
2839	cell->row = screen->lastValidRow;
2840    cell->col = (x - OriginX(screen)) / FontWidth(screen);
2841    if (cell->col < 0)
2842	cell->col = 0;
2843    else if (cell->col > MaxCols(screen)) {
2844	cell->col = MaxCols(screen);
2845    }
2846#if OPT_WIDE_CHARS
2847    /*
2848     * If we got a click on the right half of a doublewidth character,
2849     * pretend it happened on the left half.
2850     */
2851    if (cell->col > 0
2852	&& isWideCell(cell->row, cell->col - 1)
2853	&& (XTERM_CELL(cell->row, cell->col) == HIDDEN_CHAR)) {
2854	cell->col -= 1;
2855    }
2856#endif
2857}
2858
2859/*
2860 * Find the last column at which text was drawn on the given row.
2861 */
2862static int
2863LastTextCol(TScreen *screen, CLineData *ld, int row)
2864{
2865    int i = -1;
2866    const IAttr *ch;
2867
2868    if (ld != 0) {
2869	if (okScrnRow(screen, row)) {
2870	    for (i = screen->max_col,
2871		 ch = ld->attribs + i;
2872		 i >= 0 && !(*ch & CHARDRAWN);
2873		 ch--, i--) {
2874		;
2875	    }
2876#if OPT_DEC_CHRSET
2877	    if (CSET_DOUBLE(GetLineDblCS(ld))) {
2878		i *= 2;
2879	    }
2880#endif
2881	}
2882    }
2883    return (i);
2884}
2885
2886#if !OPT_WIDE_CHARS
2887/*
2888** double click table for cut and paste in 8 bits
2889**
2890** This table is divided in four parts :
2891**
2892**	- control characters	[0,0x1f] U [0x80,0x9f]
2893**	- separators		[0x20,0x3f] U [0xa0,0xb9]
2894**	- binding characters	[0x40,0x7f] U [0xc0,0xff]
2895**	- exceptions
2896*/
2897/* *INDENT-OFF* */
2898static int charClass[256] =
2899{
2900/* NUL  SOH  STX  ETX  EOT  ENQ  ACK  BEL */
2901    32,  1,    1,   1,   1,   1,   1,   1,
2902/*  BS   HT   NL   VT   FF   CR   SO   SI */
2903     1,  32,   1,   1,   1,   1,   1,   1,
2904/* DLE  DC1  DC2  DC3  DC4  NAK  SYN  ETB */
2905     1,   1,   1,   1,   1,   1,   1,   1,
2906/* CAN   EM  SUB  ESC   FS   GS   RS   US */
2907     1,   1,   1,   1,   1,   1,   1,   1,
2908/*  SP    !    "    #    $    %    &    ' */
2909    32,  33,  34,  35,  36,  37,  38,  39,
2910/*   (    )    *    +    ,    -    .    / */
2911    40,  41,  42,  43,  44,  45,  46,  47,
2912/*   0    1    2    3    4    5    6    7 */
2913    48,  48,  48,  48,  48,  48,  48,  48,
2914/*   8    9    :    ;    <    =    >    ? */
2915    48,  48,  58,  59,  60,  61,  62,  63,
2916/*   @    A    B    C    D    E    F    G */
2917    64,  48,  48,  48,  48,  48,  48,  48,
2918/*   H    I    J    K    L    M    N    O */
2919    48,  48,  48,  48,  48,  48,  48,  48,
2920/*   P    Q    R    S    T    U    V    W */
2921    48,  48,  48,  48,  48,  48,  48,  48,
2922/*   X    Y    Z    [    \    ]    ^    _ */
2923    48,  48,  48,  91,  92,  93,  94,  48,
2924/*   `    a    b    c    d    e    f    g */
2925    96,  48,  48,  48,  48,  48,  48,  48,
2926/*   h    i    j    k    l    m    n    o */
2927    48,  48,  48,  48,  48,  48,  48,  48,
2928/*   p    q    r    s    t    u    v    w */
2929    48,  48,  48,  48,  48,  48,  48,  48,
2930/*   x    y    z    {    |    }    ~  DEL */
2931    48,  48,  48, 123, 124, 125, 126,   1,
2932/* x80  x81  x82  x83  IND  NEL  SSA  ESA */
2933    1,    1,   1,   1,   1,   1,   1,   1,
2934/* HTS  HTJ  VTS  PLD  PLU   RI  SS2  SS3 */
2935    1,    1,   1,   1,   1,   1,   1,   1,
2936/* DCS  PU1  PU2  STS  CCH   MW  SPA  EPA */
2937    1,    1,   1,   1,   1,   1,   1,   1,
2938/* x98  x99  x9A  CSI   ST  OSC   PM  APC */
2939    1,    1,   1,   1,   1,   1,   1,   1,
2940/*   -    i   c/    L   ox   Y-    |   So */
2941    160, 161, 162, 163, 164, 165, 166, 167,
2942/*  ..   c0   ip   <<    _        R0    - */
2943    168, 169, 170, 171, 172, 173, 174, 175,
2944/*   o   +-    2    3    '    u   q|    . */
2945    176, 177, 178, 179, 180, 181, 182, 183,
2946/*   ,    1    2   >>  1/4  1/2  3/4    ? */
2947    184, 185, 186, 187, 188, 189, 190, 191,
2948/*  A`   A'   A^   A~   A:   Ao   AE   C, */
2949     48,  48,  48,  48,  48,  48,  48,  48,
2950/*  E`   E'   E^   E:   I`   I'   I^   I: */
2951     48,  48,  48,  48,  48,  48,  48,  48,
2952/*  D-   N~   O`   O'   O^   O~   O:    X */
2953     48,  48,  48,  48,  48,  48,  48, 215,
2954/*  O/   U`   U'   U^   U:   Y'    P    B */
2955     48,  48,  48,  48,  48,  48,  48,  48,
2956/*  a`   a'   a^   a~   a:   ao   ae   c, */
2957     48,  48,  48,  48,  48,  48,  48,  48,
2958/*  e`   e'   e^   e:    i`  i'   i^   i: */
2959     48,  48,  48,  48,  48,  48,  48,  48,
2960/*   d   n~   o`   o'   o^   o~   o:   -: */
2961     48,  48,  48,  48,  48,  48,  48, 247,
2962/*  o/   u`   u'   u^   u:   y'    P   y: */
2963     48,  48,  48,  48,  48,  48,  48,  48};
2964/* *INDENT-ON* */
2965
2966int
2967SetCharacterClassRange(int low,	/* in range of [0..255] */
2968		       int high,
2969		       int value)	/* arbitrary */
2970{
2971
2972    if (low < 0 || high > 255 || high < low)
2973	return (-1);
2974
2975    for (; low <= high; low++)
2976	charClass[low] = value;
2977
2978    return (0);
2979}
2980#endif
2981
2982static int
2983class_of(LineData *ld, CELL *cell)
2984{
2985    CELL temp = *cell;
2986    int result = 0;
2987
2988#if OPT_DEC_CHRSET
2989    if (CSET_DOUBLE(GetLineDblCS(ld))) {
2990	temp.col /= 2;
2991    }
2992#endif
2993    if (temp.col < (int) ld->lineSize)
2994	result = CharacterClass((int) (ld->charData[temp.col]));
2995    return result;
2996}
2997
2998#if OPT_WIDE_CHARS
2999#define CClassSelects(name, cclass) \
3000	 (CClassOf(name) == cclass \
3001	 || XTERM_CELL(screen->name.row, screen->name.col) == HIDDEN_CHAR)
3002#else
3003#define CClassSelects(name, cclass) \
3004	 (class_of(ld.name, &((screen->name))) == cclass)
3005#endif
3006
3007#define CClassOf(name) class_of(ld.name, &((screen->name)))
3008
3009/*
3010 * If the given column is past the end of text on the given row, bump to the
3011 * beginning of the next line.
3012 */
3013static Boolean
3014okPosition(TScreen *screen,
3015	   LineData **ld,
3016	   CELL *cell)
3017{
3018    Boolean result = True;
3019
3020    if (cell->row > screen->max_row) {
3021	result = False;
3022    } else if (cell->col > (LastTextCol(screen, *ld, cell->row) + 1)) {
3023	if (cell->row < screen->max_row) {
3024	    cell->col = 0;
3025	    *ld = GET_LINEDATA(screen, ++cell->row);
3026	    result = False;
3027	}
3028    }
3029    return result;
3030}
3031
3032static void
3033trimLastLine(TScreen *screen,
3034	     LineData **ld,
3035	     CELL *last)
3036{
3037    if (screen->cutNewline && last->row < screen->max_row) {
3038	last->col = 0;
3039	*ld = GET_LINEDATA(screen, ++last->row);
3040    } else {
3041	last->col = LastTextCol(screen, *ld, last->row) + 1;
3042    }
3043}
3044
3045#if OPT_SELECT_REGEX
3046/*
3047 * Returns the first row of a wrapped line.
3048 */
3049static int
3050firstRowOfLine(TScreen *screen, int row, Bool visible)
3051{
3052    LineData *ld = 0;
3053    int limit = visible ? 0 : -screen->savedlines;
3054
3055    while (row > limit &&
3056	   (ld = GET_LINEDATA(screen, row - 1)) != 0 &&
3057	   LineTstWrapped(ld)) {
3058	--row;
3059    }
3060    return row;
3061}
3062
3063/*
3064 * Returns the last row of a wrapped line.
3065 */
3066static int
3067lastRowOfLine(TScreen *screen, int row)
3068{
3069    LineData *ld;
3070
3071    while (row < screen->max_row &&
3072	   (ld = GET_LINEDATA(screen, row)) != 0 &&
3073	   LineTstWrapped(ld)) {
3074	++row;
3075    }
3076    return row;
3077}
3078
3079/*
3080 * Returns the number of cells on the range of rows.
3081 */
3082static unsigned
3083lengthOfLines(TScreen *screen, int firstRow, int lastRow)
3084{
3085    unsigned length = 0;
3086    int n;
3087
3088    for (n = firstRow; n <= lastRow; ++n) {
3089	LineData *ld = GET_LINEDATA(screen, n);
3090	int value = LastTextCol(screen, ld, n);
3091	if (value >= 0)
3092	    length += (unsigned) (value + 1);
3093    }
3094    return length;
3095}
3096
3097/*
3098 * Make a copy of the wrapped-line which corresponds to the given row as a
3099 * string of bytes.  Construct an index for the columns from the beginning of
3100 * the line.
3101 */
3102static char *
3103make_indexed_text(TScreen *screen, int row, unsigned length, int *indexed)
3104{
3105    Char *result = 0;
3106    size_t need = (length + 1);
3107
3108    /*
3109     * Get a quick upper bound to the number of bytes needed, if the whole
3110     * string were UTF-8.
3111     */
3112    if_OPT_WIDE_CHARS(screen, {
3113	need *= ((screen->lineExtra + 1) * 6);
3114    });
3115
3116    if ((result = TypeCallocN(Char, need + 1)) != 0) {
3117	LineData *ld = GET_LINEDATA(screen, row);
3118	unsigned used = 0;
3119	Char *last = result;
3120
3121	do {
3122	    int col = 0;
3123	    int limit = LastTextCol(screen, ld, row);
3124
3125	    while (col <= limit) {
3126		Char *next = last;
3127		unsigned data = ld->charData[col];
3128
3129		assert(col < (int) ld->lineSize);
3130		/* some internal points may not be drawn */
3131		if (data == 0)
3132		    data = ' ';
3133
3134		if_WIDE_OR_NARROW(screen, {
3135		    next = convertToUTF8(last, data);
3136		}
3137		, {
3138		    *next++ = CharOf(data);
3139		});
3140
3141		if_OPT_WIDE_CHARS(screen, {
3142		    size_t off;
3143		    for_each_combData(off, ld) {
3144			data = ld->combData[off][col];
3145			if (data == 0)
3146			    break;
3147			next = convertToUTF8(next, data);
3148		    }
3149		});
3150
3151		indexed[used] = (int) (last - result);
3152		*next = 0;
3153		/* TRACE(("index[%d.%d] %d:%s\n", row, used, indexed[used], last)); */
3154		last = next;
3155		++used;
3156		++col;
3157		indexed[used] = (int) (next - result);
3158	    }
3159	} while (used < length &&
3160		 LineTstWrapped(ld) &&
3161		 (ld = GET_LINEDATA(screen, ++row)) != 0 &&
3162		 row < screen->max_row);
3163    }
3164    /* TRACE(("result:%s\n", result)); */
3165    return (char *) result;
3166}
3167
3168/*
3169 * Find the column given an offset into the character string by using the
3170 * index constructed in make_indexed_text().
3171 */
3172static int
3173indexToCol(int *indexed, int len, int off)
3174{
3175    int col = 0;
3176    while (indexed[col] < len) {
3177	if (indexed[col] >= off)
3178	    break;
3179	++col;
3180    }
3181    return col;
3182}
3183
3184/*
3185 * Given a row number, and a column offset from that (which may be wrapped),
3186 * set the cell to the actual row/column values.
3187 */
3188static void
3189columnToCell(TScreen *screen, int row, int col, CELL *cell)
3190{
3191    while (row < screen->max_row) {
3192	CLineData *ld = GET_LINEDATA(screen, row);
3193	int last = LastTextCol(screen, ld, row);
3194
3195	/* TRACE(("last(%d) = %d, have %d\n", row, last, col)); */
3196	if (col <= last) {
3197	    break;
3198	}
3199	/*
3200	 * Stop if the current row does not wrap (does not continue the current
3201	 * line).
3202	 */
3203	if (!LineTstWrapped(ld)) {
3204	    col = last + 1;
3205	    break;
3206	}
3207	col -= (last + 1);
3208	++row;
3209    }
3210    if (col < 0)
3211	col = 0;
3212    cell->row = row;
3213    cell->col = col;
3214}
3215
3216/*
3217 * Given a cell, find the corresponding column offset.
3218 */
3219static int
3220cellToColumn(TScreen *screen, CELL *cell)
3221{
3222    CLineData *ld = 0;
3223    int col = cell->col;
3224    int row = firstRowOfLine(screen, cell->row, False);
3225    while (row < cell->row) {
3226	ld = GET_LINEDATA(screen, row);
3227	col += LastTextCol(screen, ld, row++);
3228    }
3229#if OPT_DEC_CHRSET
3230    if (ld == 0)
3231	ld = GET_LINEDATA(screen, row);
3232    if (CSET_DOUBLE(GetLineDblCS(ld)))
3233	col /= 2;
3234#endif
3235    return col;
3236}
3237
3238static void
3239do_select_regex(TScreen *screen, CELL *startc, CELL *endc)
3240{
3241    LineData *ld = GET_LINEDATA(screen, startc->row);
3242    int inx = ((screen->numberOfClicks - 1) % screen->maxClicks);
3243    char *expr = screen->selectExpr[inx];
3244    regex_t preg;
3245    regmatch_t match;
3246    char *search;
3247    int *indexed;
3248
3249    TRACE(("Select_REGEX[%d]:%s\n", inx, NonNull(expr)));
3250    if (okPosition(screen, &ld, startc) && expr != 0) {
3251	if (regcomp(&preg, expr, REG_EXTENDED) == 0) {
3252	    int firstRow = firstRowOfLine(screen, startc->row, True);
3253	    int lastRow = lastRowOfLine(screen, firstRow);
3254	    unsigned size = lengthOfLines(screen, firstRow, lastRow);
3255	    int actual = cellToColumn(screen, startc);
3256
3257	    TRACE(("regcomp ok rows %d..%d bytes %d\n",
3258		   firstRow, lastRow, size));
3259
3260	    if ((indexed = TypeCallocN(int, size + 1)) != 0) {
3261		if ((search = make_indexed_text(screen,
3262						firstRow,
3263						size,
3264						indexed)) != 0) {
3265		    int len = (int) strlen(search);
3266		    int col;
3267		    int best_col = -1;
3268		    int best_len = -1;
3269
3270		    for (col = 0; indexed[col] < len; ++col) {
3271			if (regexec(&preg,
3272				    search + indexed[col],
3273				    (size_t) 1, &match, 0) == 0) {
3274			    int start_inx = (int) (match.rm_so + indexed[col]);
3275			    int finis_inx = (int) (match.rm_eo + indexed[col]);
3276			    int start_col = indexToCol(indexed, len, start_inx);
3277			    int finis_col = indexToCol(indexed, len, finis_inx);
3278
3279			    if (start_col <= actual &&
3280				actual < finis_col) {
3281				int test = finis_col - start_col;
3282				if (best_len < test) {
3283				    best_len = test;
3284				    best_col = start_col;
3285				    TRACE(("match column %d len %d\n",
3286					   best_col,
3287					   best_len));
3288				}
3289			    }
3290			}
3291		    }
3292		    if (best_col >= 0) {
3293			int best_nxt = best_col + best_len;
3294			columnToCell(screen, firstRow, best_col, startc);
3295			columnToCell(screen, firstRow, best_nxt, endc);
3296			TRACE(("search::%s\n", search));
3297			TRACE(("indexed:%d..%d -> %d..%d\n",
3298			       best_col, best_nxt,
3299			       indexed[best_col],
3300			       indexed[best_nxt]));
3301			TRACE(("matched:%d:%s\n",
3302			       indexed[best_nxt] + 1 -
3303			       indexed[best_col],
3304			       visibleChars((Char *) (search + indexed[best_col]),
3305					    (unsigned) (indexed[best_nxt] +
3306							1 -
3307							indexed[best_col]))));
3308		    }
3309		    free(search);
3310		}
3311		free(indexed);
3312#if OPT_DEC_CHRSET
3313		if ((ld = GET_LINEDATA(screen, startc->row)) != 0) {
3314		    if (CSET_DOUBLE(GetLineDblCS(ld)))
3315			startc->col *= 2;
3316		}
3317		if ((ld = GET_LINEDATA(screen, endc->row)) != 0) {
3318		    if (CSET_DOUBLE(GetLineDblCS(ld)))
3319			endc->col *= 2;
3320		}
3321#endif
3322	    }
3323	    regfree(&preg);
3324	}
3325    }
3326}
3327#endif /* OPT_SELECT_REGEX */
3328
3329#define InitRow(name) \
3330	ld.name = GET_LINEDATA(screen, screen->name.row)
3331
3332#define NextRow(name) \
3333	ld.name = GET_LINEDATA(screen, ++screen->name.row)
3334
3335#define PrevRow(name) \
3336	ld.name = GET_LINEDATA(screen, --screen->name.row)
3337
3338#define MoreRows(name) \
3339	(screen->name.row < screen->max_row)
3340
3341#define isPrevWrapped(name) \
3342	(screen->name.row > 0 \
3343	   && (ltmp = GET_LINEDATA(screen, screen->name.row - 1)) != 0 \
3344	   && LineTstWrapped(ltmp))
3345
3346/*
3347 * sets startSel endSel
3348 * ensuring that they have legal values
3349 */
3350static void
3351ComputeSelect(XtermWidget xw,
3352	      CELL *startc,
3353	      CELL *endc,
3354	      Bool extend)
3355{
3356    TScreen *screen = TScreenOf(xw);
3357
3358    int length;
3359    int cclass;
3360    CELL first = *startc;
3361    CELL last = *endc;
3362    Boolean ignored = False;
3363
3364    struct {
3365	LineData *startSel;
3366	LineData *endSel;
3367    } ld;
3368    LineData *ltmp;
3369
3370    TRACE(("ComputeSelect(startRow=%d, startCol=%d, endRow=%d, endCol=%d, %sextend)\n",
3371	   first.row, first.col,
3372	   last.row, last.col,
3373	   extend ? "" : "no"));
3374
3375#if OPT_WIDE_CHARS
3376    if (first.col > 1
3377	&& isWideCell(first.row, first.col - 1)
3378	&& XTERM_CELL(first.row, first.col - 0) == HIDDEN_CHAR) {
3379	TRACE(("Adjusting start. Changing downwards from %i.\n", first.col));
3380	first.col -= 1;
3381	if (last.col == (first.col + 1))
3382	    last.col--;
3383    }
3384
3385    if (last.col > 1
3386	&& isWideCell(last.row, last.col - 1)
3387	&& XTERM_CELL(last.row, last.col) == HIDDEN_CHAR) {
3388	last.col += 1;
3389    }
3390#endif
3391
3392    if (Coordinate(screen, &first) <= Coordinate(screen, &last)) {
3393	screen->startSel = screen->startRaw = first;
3394	screen->endSel = screen->endRaw = last;
3395    } else {			/* Swap them */
3396	screen->startSel = screen->startRaw = last;
3397	screen->endSel = screen->endRaw = first;
3398    }
3399
3400    InitRow(startSel);
3401    InitRow(endSel);
3402
3403    switch (screen->selectUnit) {
3404    case Select_CHAR:
3405	(void) okPosition(screen, &(ld.startSel), &(screen->startSel));
3406	(void) okPosition(screen, &(ld.endSel), &(screen->endSel));
3407	break;
3408
3409    case Select_WORD:
3410	TRACE(("Select_WORD\n"));
3411	if (okPosition(screen, &(ld.startSel), &(screen->startSel))) {
3412	    cclass = CClassOf(startSel);
3413	    do {
3414		--screen->startSel.col;
3415		if (screen->startSel.col < 0
3416		    && isPrevWrapped(startSel)) {
3417		    PrevRow(startSel);
3418		    screen->startSel.col = LastTextCol(screen, ld.startSel, screen->startSel.row);
3419		}
3420	    } while (screen->startSel.col >= 0
3421		     && CClassSelects(startSel, cclass));
3422	    ++screen->startSel.col;
3423	}
3424#if OPT_WIDE_CHARS
3425	if (screen->startSel.col
3426	    && XTERM_CELL(screen->startSel.row,
3427			  screen->startSel.col) == HIDDEN_CHAR)
3428	    screen->startSel.col++;
3429#endif
3430
3431	if (okPosition(screen, &(ld.endSel), &(screen->endSel))) {
3432	    length = LastTextCol(screen, ld.endSel, screen->endSel.row);
3433	    cclass = CClassOf(endSel);
3434	    do {
3435		++screen->endSel.col;
3436		if (screen->endSel.col > length
3437		    && LineTstWrapped(ld.endSel)) {
3438		    if (!MoreRows(endSel))
3439			break;
3440		    screen->endSel.col = 0;
3441		    NextRow(endSel);
3442		    length = LastTextCol(screen, ld.endSel, screen->endSel.row);
3443		}
3444	    } while (screen->endSel.col <= length
3445		     && CClassSelects(endSel, cclass));
3446	    /* Word-select selects if pointing to any char in "word",
3447	     * especially note that it includes the last character in a word.
3448	     * So we do no --endSel.col and do special eol handling.
3449	     */
3450	    if (screen->endSel.col > length + 1
3451		&& MoreRows(endSel)) {
3452		screen->endSel.col = 0;
3453		NextRow(endSel);
3454	    }
3455	}
3456#if OPT_WIDE_CHARS
3457	if (screen->endSel.col
3458	    && XTERM_CELL(screen->endSel.row,
3459			  screen->endSel.col) == HIDDEN_CHAR)
3460	    screen->endSel.col++;
3461#endif
3462
3463	screen->saveStartW = screen->startSel;
3464	break;
3465
3466    case Select_LINE:
3467	TRACE(("Select_LINE\n"));
3468	while (LineTstWrapped(ld.endSel)
3469	       && MoreRows(endSel)) {
3470	    NextRow(endSel);
3471	}
3472	if (screen->cutToBeginningOfLine
3473	    || screen->startSel.row < screen->saveStartW.row) {
3474	    screen->startSel.col = 0;
3475	    while (isPrevWrapped(startSel)) {
3476		PrevRow(startSel);
3477	    }
3478	} else if (!extend) {
3479	    if ((first.row < screen->saveStartW.row)
3480		|| (isSameRow(&first, &(screen->saveStartW))
3481		    && first.col < screen->saveStartW.col)) {
3482		screen->startSel.col = 0;
3483		while (isPrevWrapped(startSel)) {
3484		    PrevRow(startSel);
3485		}
3486	    } else {
3487		screen->startSel = screen->saveStartW;
3488	    }
3489	}
3490	trimLastLine(screen, &(ld.endSel), &(screen->endSel));
3491	break;
3492
3493    case Select_GROUP:		/* paragraph */
3494	TRACE(("Select_GROUP\n"));
3495	if (okPosition(screen, &(ld.startSel), &(screen->startSel))) {
3496	    /* scan backward for beginning of group */
3497	    while (screen->startSel.row > 0 &&
3498		   (LastTextCol(screen, ld.startSel, screen->startSel.row -
3499				1) > 0 ||
3500		    isPrevWrapped(startSel))) {
3501		PrevRow(startSel);
3502	    }
3503	    screen->startSel.col = 0;
3504	    /* scan forward for end of group */
3505	    while (MoreRows(endSel) &&
3506		   (LastTextCol(screen, ld.endSel, screen->endSel.row + 1) >
3507		    0 ||
3508		    LineTstWrapped(ld.endSel))) {
3509		NextRow(endSel);
3510	    }
3511	    trimLastLine(screen, &(ld.endSel), &(screen->endSel));
3512	}
3513	break;
3514
3515    case Select_PAGE:		/* everything one can see */
3516	TRACE(("Select_PAGE\n"));
3517	screen->startSel.row = 0;
3518	screen->startSel.col = 0;
3519	screen->endSel.row = MaxRows(screen);
3520	screen->endSel.col = 0;
3521	break;
3522
3523    case Select_ALL:		/* counts scrollback if in normal screen */
3524	TRACE(("Select_ALL\n"));
3525	screen->startSel.row = -screen->savedlines;
3526	screen->startSel.col = 0;
3527	screen->endSel.row = MaxRows(screen);
3528	screen->endSel.col = 0;
3529	break;
3530
3531#if OPT_SELECT_REGEX
3532    case Select_REGEX:
3533	do_select_regex(screen, &(screen->startSel), &(screen->endSel));
3534	break;
3535#endif
3536
3537    case NSELECTUNITS:		/* always ignore */
3538	ignored = True;
3539	break;
3540    }
3541
3542    if (!ignored) {
3543	/* check boundaries */
3544	ScrollSelection(screen, 0, False);
3545	TrackText(xw, &(screen->startSel), &(screen->endSel));
3546    }
3547
3548    return;
3549}
3550
3551/* Guaranteed (first.row, first.col) <= (last.row, last.col) */
3552static void
3553TrackText(XtermWidget xw,
3554	  const CELL *firstp,
3555	  const CELL *lastp)
3556{
3557    TScreen *screen = TScreenOf(xw);
3558    int from, to;
3559    CELL old_start, old_end;
3560    CELL first = *firstp;
3561    CELL last = *lastp;
3562
3563    TRACE(("TrackText(first=%d,%d, last=%d,%d)\n",
3564	   first.row, first.col, last.row, last.col));
3565
3566    old_start = screen->startH;
3567    old_end = screen->endH;
3568    TRACE(("...previous(first=%d,%d, last=%d,%d)\n",
3569	   old_start.row, old_start.col,
3570	   old_end.row, old_end.col));
3571    if (isSameCELL(&first, &old_start) &&
3572	isSameCELL(&last, &old_end)) {
3573	return;
3574    }
3575
3576    screen->startH = first;
3577    screen->endH = last;
3578    from = Coordinate(screen, &screen->startH);
3579    to = Coordinate(screen, &screen->endH);
3580    if (to <= screen->startHCoord || from > screen->endHCoord) {
3581	/* No overlap whatsoever between old and new hilite */
3582	ReHiliteText(xw, &old_start, &old_end);
3583	ReHiliteText(xw, &first, &last);
3584    } else {
3585	if (from < screen->startHCoord) {
3586	    /* Extend left end */
3587	    ReHiliteText(xw, &first, &old_start);
3588	} else if (from > screen->startHCoord) {
3589	    /* Shorten left end */
3590	    ReHiliteText(xw, &old_start, &first);
3591	}
3592	if (to > screen->endHCoord) {
3593	    /* Extend right end */
3594	    ReHiliteText(xw, &old_end, &last);
3595	} else if (to < screen->endHCoord) {
3596	    /* Shorten right end */
3597	    ReHiliteText(xw, &last, &old_end);
3598	}
3599    }
3600    screen->startHCoord = from;
3601    screen->endHCoord = to;
3602}
3603
3604/* Guaranteed that (first->row, first->col) <= (last->row, last->col) */
3605static void
3606ReHiliteText(XtermWidget xw,
3607	     CELL *firstp,
3608	     CELL *lastp)
3609{
3610    TScreen *screen = TScreenOf(xw);
3611    int i;
3612    CELL first = *firstp;
3613    CELL last = *lastp;
3614
3615    TRACE(("ReHiliteText from %d.%d to %d.%d\n",
3616	   first.row, first.col, last.row, last.col));
3617
3618    if (first.row < 0)
3619	first.row = first.col = 0;
3620    else if (first.row > screen->max_row)
3621	return;			/* nothing to do, since last.row >= first.row */
3622
3623    if (last.row < 0)
3624	return;			/* nothing to do, since first.row <= last.row */
3625    else if (last.row > screen->max_row) {
3626	last.row = screen->max_row;
3627	last.col = MaxCols(screen);
3628    }
3629    if (isSameCELL(&first, &last))
3630	return;
3631
3632    if (!isSameRow(&first, &last)) {	/* do multiple rows */
3633	if ((i = screen->max_col - first.col + 1) > 0) {	/* first row */
3634	    ScrnRefresh(xw, first.row, first.col, 1, i, True);
3635	}
3636	if ((i = last.row - first.row - 1) > 0) {	/* middle rows */
3637	    ScrnRefresh(xw, first.row + 1, 0, i, MaxCols(screen), True);
3638	}
3639	if (last.col > 0 && last.row <= screen->max_row) {	/* last row */
3640	    ScrnRefresh(xw, last.row, 0, 1, last.col, True);
3641	}
3642    } else {			/* do single row */
3643	ScrnRefresh(xw, first.row, first.col, 1, last.col - first.col, True);
3644    }
3645}
3646
3647/*
3648 * Guaranteed that (cellc->row, cellc->col) <= (cell->row, cell->col), and that both points are valid
3649 * (may have cell->row = screen->max_row+1, cell->col = 0).
3650 */
3651static void
3652SaltTextAway(XtermWidget xw,
3653	     CELL *cellc,
3654	     CELL *cell)
3655{
3656    TScreen *screen = TScreenOf(xw);
3657    int i, j = 0;
3658    int eol;
3659    int tmp;
3660    Char *line;
3661    Char *lp;
3662    CELL first = *cellc;
3663    CELL last = *cell;
3664
3665    if (isSameRow(&first, &last) && first.col > last.col) {
3666	EXCHANGE(first.col, last.col, tmp);
3667    }
3668
3669    --last.col;
3670    /* first we need to know how long the string is before we can save it */
3671
3672    if (isSameRow(&last, &first)) {
3673	j = Length(screen, first.row, first.col, last.col);
3674    } else {			/* two cases, cut is on same line, cut spans multiple lines */
3675	j += Length(screen, first.row, first.col, screen->max_col) + 1;
3676	for (i = first.row + 1; i < last.row; i++)
3677	    j += Length(screen, i, 0, screen->max_col) + 1;
3678	if (last.col >= 0)
3679	    j += Length(screen, last.row, 0, last.col);
3680    }
3681
3682    /* UTF-8 may require more space */
3683    if_OPT_WIDE_CHARS(screen, {
3684	j *= 4;
3685    });
3686
3687    /* now get some memory to save it in */
3688
3689    if (screen->selection_size <= j) {
3690	if ((line = (Char *) malloc((size_t) j + 1)) == 0)
3691	    SysError(ERROR_BMALLOC2);
3692	XtFree((char *) screen->selection_data);
3693	screen->selection_data = line;
3694	screen->selection_size = j + 1;
3695    } else {
3696	line = screen->selection_data;
3697    }
3698
3699    if ((line == 0)
3700	|| (j < 0))
3701	return;
3702
3703    line[j] = '\0';		/* make sure it is null terminated */
3704    lp = line;			/* lp points to where to save the text */
3705    if (isSameRow(&last, &first)) {
3706	lp = SaveText(screen, last.row, first.col, last.col, lp, &eol);
3707    } else {
3708	lp = SaveText(screen, first.row, first.col, screen->max_col, lp, &eol);
3709	if (eol)
3710	    *lp++ = '\n';	/* put in newline at end of line */
3711	for (i = first.row + 1; i < last.row; i++) {
3712	    lp = SaveText(screen, i, 0, screen->max_col, lp, &eol);
3713	    if (eol)
3714		*lp++ = '\n';
3715	}
3716	if (last.col >= 0)
3717	    lp = SaveText(screen, last.row, 0, last.col, lp, &eol);
3718    }
3719    *lp = '\0';			/* make sure we have end marked */
3720
3721    TRACE(("Salted TEXT:%d:%s\n", (int) (lp - line),
3722	   visibleChars(line, (unsigned) (lp - line))));
3723
3724    screen->selection_length = (unsigned long) (lp - line);
3725}
3726
3727#if OPT_PASTE64
3728void
3729ClearSelectionBuffer(TScreen *screen)
3730{
3731    screen->selection_length = 0;
3732    screen->base64_count = 0;
3733}
3734
3735static void
3736AppendStrToSelectionBuffer(TScreen *screen, Char *text, size_t len)
3737{
3738    if (len != 0) {
3739	int j = (int) (screen->selection_length + len);		/* New length */
3740	int k = j + (j >> 2) + 80;	/* New size if we grow buffer: grow by ~50% */
3741	if (j + 1 >= screen->selection_size) {
3742	    if (!screen->selection_length) {
3743		/* New buffer */
3744		Char *line;
3745		if ((line = (Char *) malloc((size_t) k)) == 0)
3746		    SysError(ERROR_BMALLOC2);
3747		XtFree((char *) screen->selection_data);
3748		screen->selection_data = line;
3749	    } else {
3750		/* Realloc buffer */
3751		screen->selection_data = (Char *)
3752		    realloc(screen->selection_data,
3753			    (size_t) k);
3754		if (screen->selection_data == 0)
3755		    SysError(ERROR_BMALLOC2);
3756	    }
3757	    screen->selection_size = k;
3758	}
3759	if (screen->selection_data != 0) {
3760	    memcpy(screen->selection_data + screen->selection_length, text, len);
3761	    screen->selection_length += len;
3762	    screen->selection_data[screen->selection_length] = 0;
3763	}
3764    }
3765}
3766
3767void
3768AppendToSelectionBuffer(TScreen *screen, unsigned c)
3769{
3770    unsigned six;
3771    Char ch;
3772
3773    /* Decode base64 character */
3774    if (c >= 'A' && c <= 'Z')
3775	six = c - 'A';
3776    else if (c >= 'a' && c <= 'z')
3777	six = c - 'a' + 26;
3778    else if (c >= '0' && c <= '9')
3779	six = c - '0' + 52;
3780    else if (c == '+')
3781	six = 62;
3782    else if (c == '/')
3783	six = 63;
3784    else
3785	return;
3786
3787    /* Accumulate bytes */
3788    switch (screen->base64_count) {
3789    case 0:
3790	screen->base64_accu = six;
3791	screen->base64_count = 6;
3792	break;
3793
3794    case 2:
3795	ch = CharOf((screen->base64_accu << 6) + six);
3796	screen->base64_count = 0;
3797	AppendStrToSelectionBuffer(screen, &ch, (size_t) 1);
3798	break;
3799
3800    case 4:
3801	ch = CharOf((screen->base64_accu << 4) + (six >> 2));
3802	screen->base64_accu = (six & 0x3);
3803	screen->base64_count = 2;
3804	AppendStrToSelectionBuffer(screen, &ch, (size_t) 1);
3805	break;
3806
3807    case 6:
3808	ch = CharOf((screen->base64_accu << 2) + (six >> 4));
3809	screen->base64_accu = (six & 0xF);
3810	screen->base64_count = 4;
3811	AppendStrToSelectionBuffer(screen, &ch, (size_t) 1);
3812	break;
3813    }
3814}
3815
3816void
3817CompleteSelection(XtermWidget xw, String *args, Cardinal len)
3818{
3819    TScreen *screen = TScreenOf(xw);
3820
3821    screen->base64_count = 0;
3822    screen->base64_accu = 0;
3823    _OwnSelection(xw, args, len);
3824}
3825#endif /* OPT_PASTE64 */
3826
3827static Bool
3828_ConvertSelectionHelper(Widget w,
3829			Atom *type,
3830			XtPointer *value,
3831			unsigned long *length,
3832			int *format,
3833			int (*conversion_function) (Display *,
3834						    char **, int,
3835						    XICCEncodingStyle,
3836						    XTextProperty *),
3837			XICCEncodingStyle conversion_style)
3838{
3839    XtermWidget xw;
3840
3841    *value = 0;
3842    *length = 0;
3843    *type = 0;
3844    *format = 0;
3845
3846    if ((xw = getXtermWidget(w)) != 0) {
3847	TScreen *screen = TScreenOf(xw);
3848	Display *dpy = XtDisplay(w);
3849	XTextProperty textprop;
3850	int out_n = 0;
3851	unsigned long remaining = screen->selection_length;
3852	char *result = 0;
3853	char *the_data = (char *) screen->selection_data;
3854	char *the_next;
3855
3856	TRACE(("converting %ld:'%s'\n",
3857	       (long) screen->selection_length,
3858	       visibleChars(screen->selection_data, (unsigned) screen->selection_length)));
3859	/*
3860	 * For most selections, we can convert in one pass.  It is possible
3861	 * that some applications contain embedded nulls, e.g., using xterm's
3862	 * paste64 feature.  For those cases, we will build up the result in
3863	 * parts.
3864	 */
3865	if (memchr(the_data, 0, screen->selection_length) != 0) {
3866	    TRACE(("selection contains embedded nulls\n"));
3867	    result = calloc(screen->selection_length + 1, sizeof(char));
3868	}
3869
3870      next_try:
3871	memset(&textprop, 0, sizeof(textprop));
3872	if (conversion_function(dpy, &the_data, 1,
3873				conversion_style,
3874				&textprop) >= Success) {
3875	    if ((result != 0)
3876		&& (textprop.value != 0)
3877		&& (textprop.format == 8)) {
3878		char *text_values = (char *) textprop.value;
3879		unsigned long in_n;
3880
3881		if (out_n == 0) {
3882		    *value = result;
3883		    *type = textprop.encoding;
3884		    *format = textprop.format;
3885		}
3886		for (in_n = 0; in_n < textprop.nitems; ++in_n) {
3887		    result[out_n++] = text_values[in_n];
3888		}
3889		*length += textprop.nitems;
3890		if ((the_next = memchr(the_data, 0, remaining)) != 0) {
3891		    unsigned long this_was = (unsigned long) (the_next - the_data);
3892		    this_was++;
3893		    the_data += this_was;
3894		    remaining -= this_was;
3895		    result[out_n++] = 0;
3896		    *length += 1;
3897		    if (remaining)
3898			goto next_try;
3899		}
3900		return True;
3901	    } else {
3902		free(result);
3903		*value = (XtPointer) textprop.value;
3904		*length = textprop.nitems;
3905		*type = textprop.encoding;
3906		*format = textprop.format;
3907		return True;
3908	    }
3909	}
3910	free(result);
3911    }
3912    return False;
3913}
3914
3915static Boolean
3916SaveConvertedLength(XtPointer *target, unsigned long source)
3917{
3918    Boolean result = False;
3919
3920    *target = XtMalloc(4);
3921    if (*target != 0) {
3922	result = True;
3923	if (sizeof(unsigned long) == 4) {
3924	    *(unsigned long *) *target = source;
3925	} else if (sizeof(unsigned) == 4) {
3926	    *(unsigned *) *target = (unsigned) source;
3927	} else if (sizeof(unsigned short) == 4) {
3928	    *(unsigned short *) *target = (unsigned short) source;
3929	} else {
3930	    /* FIXME - does this depend on byte-order? */
3931	    unsigned long temp = source;
3932	    memcpy((char *) *target,
3933		   ((char *) &temp) + sizeof(temp) - 4,
3934		   (size_t) 4);
3935	}
3936    }
3937    return result;
3938}
3939
3940static Boolean
3941ConvertSelection(Widget w,
3942		 Atom *selection,
3943		 Atom *target,
3944		 Atom *type,
3945		 XtPointer *value,
3946		 unsigned long *length,
3947		 int *format)
3948{
3949    Display *dpy = XtDisplay(w);
3950    TScreen *screen;
3951    Bool result = False;
3952
3953    XtermWidget xw;
3954
3955    if ((xw = getXtermWidget(w)) == 0)
3956	return False;
3957
3958    screen = TScreenOf(xw);
3959
3960    TRACE(("ConvertSelection %s\n",
3961	   visibleSelectionTarget(dpy, *target)));
3962
3963    if (screen->selection_data == NULL) {
3964	TRACE(("...FIXME: no selection_data\n"));
3965	return False;		/* can this happen? */
3966    }
3967
3968    if (*target == XA_TARGETS(dpy)) {
3969	Atom *allocP;
3970	Atom *targetP;
3971	Atom *std_targets;
3972	XPointer std_return = 0;
3973	unsigned long std_length;
3974
3975	if (XmuConvertStandardSelection(w, screen->selection_time, selection,
3976					target, type, &std_return,
3977					&std_length, format)) {
3978	    Atom *my_targets = _SelectionTargets(w);
3979
3980	    TRACE(("XmuConvertStandardSelection - success\n"));
3981	    std_targets = (Atom *) (void *) (std_return);
3982	    *length = std_length + 6;
3983
3984	    targetP = TypeXtMallocN(Atom, *length);
3985	    allocP = targetP;
3986
3987	    *value = (XtPointer) targetP;
3988
3989	    if (my_targets != 0) {
3990		while (*my_targets != None) {
3991		    *targetP++ = *my_targets++;
3992		}
3993	    }
3994	    *targetP++ = XA_LENGTH(dpy);
3995	    *targetP++ = XA_LIST_LENGTH(dpy);
3996
3997	    *length = std_length + (unsigned long) (targetP - allocP);
3998
3999	    memcpy(targetP, std_targets, sizeof(Atom) * std_length);
4000	    XtFree((char *) std_targets);
4001	    *type = XA_ATOM;
4002	    *format = 32;
4003	    result = True;
4004	} else {
4005	    TRACE(("XmuConvertStandardSelection - failed\n"));
4006	}
4007    }
4008#if OPT_WIDE_CHARS
4009    else if (screen->wide_chars && *target == XA_STRING) {
4010	result =
4011	    _ConvertSelectionHelper(w,
4012				    type, value, length, format,
4013				    Xutf8TextListToTextProperty,
4014				    XStringStyle);
4015	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4016    } else if (screen->wide_chars && *target == XA_UTF8_STRING(dpy)) {
4017	result =
4018	    _ConvertSelectionHelper(w,
4019				    type, value, length, format,
4020				    Xutf8TextListToTextProperty,
4021				    XUTF8StringStyle);
4022	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4023    } else if (screen->wide_chars && *target == XA_TEXT(dpy)) {
4024	result =
4025	    _ConvertSelectionHelper(w,
4026				    type, value, length, format,
4027				    Xutf8TextListToTextProperty,
4028				    XStdICCTextStyle);
4029	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4030    } else if (screen->wide_chars && *target == XA_COMPOUND_TEXT(dpy)) {
4031	result =
4032	    _ConvertSelectionHelper(w,
4033				    type, value, length, format,
4034				    Xutf8TextListToTextProperty,
4035				    XCompoundTextStyle);
4036	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4037    }
4038#endif
4039
4040    else if (*target == XA_STRING) {	/* not wide_chars */
4041	/* We can only reach this point if the selection requestor
4042	   requested STRING before any of TEXT, COMPOUND_TEXT or
4043	   UTF8_STRING.  We therefore assume that the requestor is not
4044	   properly internationalised, and dump raw eight-bit data
4045	   with no conversion into the selection.  Yes, this breaks
4046	   the ICCCM in non-Latin-1 locales. */
4047	*type = XA_STRING;
4048	*value = (XtPointer) screen->selection_data;
4049	*length = screen->selection_length;
4050	*format = 8;
4051	result = True;
4052	TRACE(("...raw 8-bit data:%d\n", result));
4053    } else if (*target == XA_TEXT(dpy)) {	/* not wide_chars */
4054	result =
4055	    _ConvertSelectionHelper(w,
4056				    type, value, length, format,
4057				    XmbTextListToTextProperty,
4058				    XStdICCTextStyle);
4059	TRACE(("...XmbTextListToTextProperty(StdICC):%d\n", result));
4060    } else if (*target == XA_COMPOUND_TEXT(dpy)) {	/* not wide_chars */
4061	result =
4062	    _ConvertSelectionHelper(w,
4063				    type, value, length, format,
4064				    XmbTextListToTextProperty,
4065				    XCompoundTextStyle);
4066	TRACE(("...XmbTextListToTextProperty(Compound):%d\n", result));
4067    }
4068#ifdef X_HAVE_UTF8_STRING
4069    else if (*target == XA_UTF8_STRING(dpy)) {	/* not wide_chars */
4070	result =
4071	    _ConvertSelectionHelper(w,
4072				    type, value, length, format,
4073				    XmbTextListToTextProperty,
4074				    XUTF8StringStyle);
4075	TRACE(("...XmbTextListToTextProperty(UTF8):%d\n", result));
4076    }
4077#endif
4078    else if (*target == XA_LIST_LENGTH(dpy)) {
4079	result = SaveConvertedLength(value, (unsigned long) 1);
4080	*type = XA_INTEGER;
4081	*length = 1;
4082	*format = 32;
4083	TRACE(("...list of values:%d\n", result));
4084    } else if (*target == XA_LENGTH(dpy)) {
4085	/* This value is wrong if we have UTF-8 text */
4086	result = SaveConvertedLength(value, screen->selection_length);
4087	*type = XA_INTEGER;
4088	*length = 1;
4089	*format = 32;
4090	TRACE(("...list of values:%d\n", result));
4091    } else if (XmuConvertStandardSelection(w,
4092					   screen->selection_time, selection,
4093					   target, type, (XPointer *) value,
4094					   length, format)) {
4095	result = True;
4096	TRACE(("...XmuConvertStandardSelection:%d\n", result));
4097    }
4098
4099    /* else */
4100    return (Boolean) result;
4101}
4102
4103static void
4104LoseSelection(Widget w, Atom *selection)
4105{
4106    TScreen *screen;
4107    Atom *atomP;
4108    Cardinal i;
4109
4110    XtermWidget xw;
4111
4112    if ((xw = getXtermWidget(w)) == 0)
4113	return;
4114
4115    screen = TScreenOf(xw);
4116    TRACE(("LoseSelection %s\n", XGetAtomName(screen->display, *selection)));
4117
4118    for (i = 0, atomP = screen->selection_atoms;
4119	 i < screen->selection_count; i++, atomP++) {
4120	if (*selection == *atomP)
4121	    *atomP = (Atom) 0;
4122	if (CutBuffer(*atomP) >= 0) {
4123	    *atomP = (Atom) 0;
4124	}
4125    }
4126
4127    for (i = screen->selection_count; i; i--) {
4128	if (screen->selection_atoms[i - 1] != 0)
4129	    break;
4130    }
4131    screen->selection_count = i;
4132
4133    for (i = 0, atomP = screen->selection_atoms;
4134	 i < screen->selection_count; i++, atomP++) {
4135	if (*atomP == (Atom) 0) {
4136	    *atomP = screen->selection_atoms[--screen->selection_count];
4137	}
4138    }
4139
4140    if (screen->selection_count == 0)
4141	TrackText(xw, &zeroCELL, &zeroCELL);
4142}
4143
4144/* ARGSUSED */
4145static void
4146SelectionDone(Widget w GCC_UNUSED,
4147	      Atom *selection GCC_UNUSED,
4148	      Atom *target GCC_UNUSED)
4149{
4150    /* empty proc so Intrinsics know we want to keep storage */
4151    TRACE(("SelectionDone\n"));
4152}
4153
4154static void
4155_OwnSelection(XtermWidget xw,
4156	      String *selections,
4157	      Cardinal count)
4158{
4159    TScreen *screen = TScreenOf(xw);
4160    Atom *atoms = screen->selection_atoms;
4161    Cardinal i;
4162    Bool have_selection = False;
4163
4164    if (count == 0)
4165	return;
4166
4167    TRACE(("_OwnSelection count %d, length %ld value %s\n", count,
4168	   screen->selection_length,
4169	   visibleChars(screen->selection_data, (unsigned) screen->selection_length)));
4170    selections = MapSelections(xw, selections, count);
4171
4172    if (count > screen->sel_atoms_size) {
4173	XtFree((char *) atoms);
4174	atoms = TypeXtMallocN(Atom, count);
4175	screen->selection_atoms = atoms;
4176	screen->sel_atoms_size = count;
4177    }
4178    XmuInternStrings(XtDisplay((Widget) xw), selections, count, atoms);
4179    for (i = 0; i < count; i++) {
4180	int cutbuffer = CutBuffer(atoms[i]);
4181	if (cutbuffer >= 0) {
4182	    unsigned long limit =
4183	    (unsigned long) (4 * XMaxRequestSize(XtDisplay((Widget) xw)) - 32);
4184	    if (screen->selection_length > limit) {
4185		TRACE(("selection too big (%lu bytes), not storing in CUT_BUFFER%d\n",
4186		       screen->selection_length, cutbuffer));
4187		xtermWarning("selection too big (%lu bytes), not storing in CUT_BUFFER%d\n",
4188			     screen->selection_length, cutbuffer);
4189	    } else {
4190		/* This used to just use the UTF-8 data, which was totally
4191		 * broken as not even the corresponding paste code in xterm
4192		 * understood this!  So now it converts to Latin1 first.
4193		 *   Robert Brady, 2000-09-05
4194		 */
4195		unsigned long length = screen->selection_length;
4196		Char *data = screen->selection_data;
4197		if_OPT_WIDE_CHARS((screen), {
4198		    data = UTF8toLatin1(screen, data, length, &length);
4199		});
4200		TRACE(("XStoreBuffer(%d)\n", cutbuffer));
4201		XStoreBuffer(XtDisplay((Widget) xw),
4202			     (char *) data,
4203			     (int) length,
4204			     cutbuffer);
4205	    }
4206	} else if (screen->selection_length == 0) {
4207	    XtDisownSelection((Widget) xw, atoms[i], screen->selection_time);
4208	} else if (!screen->replyToEmacs) {
4209	    have_selection |=
4210		XtOwnSelection((Widget) xw, atoms[i],
4211			       screen->selection_time,
4212			       ConvertSelection, LoseSelection, SelectionDone);
4213	}
4214    }
4215    if (!screen->replyToEmacs)
4216	screen->selection_count = count;
4217    if (!have_selection)
4218	TrackText(xw, &zeroCELL, &zeroCELL);
4219}
4220
4221static void
4222ResetSelectionState(TScreen *screen)
4223{
4224    screen->selection_count = 0;
4225    screen->startH = zeroCELL;
4226    screen->endH = zeroCELL;
4227}
4228
4229void
4230DisownSelection(XtermWidget xw)
4231{
4232    TScreen *screen = TScreenOf(xw);
4233    Atom *atoms = screen->selection_atoms;
4234    Cardinal count = screen->selection_count;
4235    Cardinal i;
4236
4237    TRACE(("DisownSelection count %d, start %d.%d, end %d.%d\n",
4238	   count,
4239	   screen->startH.row,
4240	   screen->startH.col,
4241	   screen->endH.row,
4242	   screen->endH.col));
4243
4244    for (i = 0; i < count; i++) {
4245	int cutbuffer = CutBuffer(atoms[i]);
4246	if (cutbuffer < 0) {
4247	    XtDisownSelection((Widget) xw, atoms[i],
4248			      screen->selection_time);
4249	}
4250    }
4251    /*
4252     * If none of the callbacks via XtDisownSelection() reset highlighting
4253     * do it now.
4254     */
4255    if (ScrnHaveSelection(screen)) {
4256	/* save data which will be reset */
4257	CELL first = screen->startH;
4258	CELL last = screen->endH;
4259
4260	ResetSelectionState(screen);
4261	ReHiliteText(xw, &first, &last);
4262    } else {
4263	ResetSelectionState(screen);
4264    }
4265}
4266
4267void
4268UnhiliteSelection(XtermWidget xw)
4269{
4270    TScreen *screen = TScreenOf(xw);
4271
4272    if (ScrnHaveSelection(screen)) {
4273	CELL first = screen->startH;
4274	CELL last = screen->endH;
4275
4276	screen->startH = zeroCELL;
4277	screen->endH = zeroCELL;
4278	ReHiliteText(xw, &first, &last);
4279    }
4280}
4281
4282/* returns number of chars in line from scol to ecol out */
4283/* ARGSUSED */
4284static int
4285Length(TScreen *screen,
4286       int row,
4287       int scol,
4288       int ecol)
4289{
4290    CLineData *ld = GET_LINEDATA(screen, row);
4291    const int lastcol = LastTextCol(screen, ld, row);
4292
4293    if (ecol > lastcol)
4294	ecol = lastcol;
4295    return (ecol - scol + 1);
4296}
4297
4298/* copies text into line, preallocated */
4299static Char *
4300SaveText(TScreen *screen,
4301	 int row,
4302	 int scol,
4303	 int ecol,
4304	 Char *lp,		/* pointer to where to put the text */
4305	 int *eol)
4306{
4307    LineData *ld;
4308    int i = 0;
4309    unsigned c;
4310    Char *result = lp;
4311#if OPT_WIDE_CHARS
4312    unsigned previous = 0;
4313#endif
4314
4315    ld = GET_LINEDATA(screen, row);
4316    i = Length(screen, row, scol, ecol);
4317    ecol = scol + i;
4318#if OPT_DEC_CHRSET
4319    if (CSET_DOUBLE(GetLineDblCS(ld))) {
4320	scol = (scol + 0) / 2;
4321	ecol = (ecol + 1) / 2;
4322    }
4323#endif
4324    *eol = !LineTstWrapped(ld);
4325    for (i = scol; i < ecol; i++) {
4326	assert(i < (int) ld->lineSize);
4327	c = E2A(ld->charData[i]);
4328#if OPT_WIDE_CHARS
4329	/* We want to strip out every occurrence of HIDDEN_CHAR AFTER a
4330	 * wide character.
4331	 */
4332	if (c == HIDDEN_CHAR) {
4333	    if (isWide((int) previous)) {
4334		previous = c;
4335		/* Combining characters attached to double-width characters
4336		   are in memory attached to the HIDDEN_CHAR */
4337		if_OPT_WIDE_CHARS(screen, {
4338		    if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
4339			unsigned ch;
4340			size_t off;
4341			for_each_combData(off, ld) {
4342			    ch = ld->combData[off][i];
4343			    if (ch == 0)
4344				break;
4345			    lp = convertToUTF8(lp, ch);
4346			}
4347		    }
4348		});
4349		continue;
4350	    } else {
4351		c = ' ';	/* should not happen, but just in case... */
4352	    }
4353	}
4354	previous = c;
4355	if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
4356	    lp = convertToUTF8(lp, (c != 0) ? c : ' ');
4357	    if_OPT_WIDE_CHARS(screen, {
4358		unsigned ch;
4359		size_t off;
4360		for_each_combData(off, ld) {
4361		    ch = ld->combData[off][i];
4362		    if (ch == 0)
4363			break;
4364		    lp = convertToUTF8(lp, ch);
4365		}
4366	    });
4367	} else
4368#endif
4369	{
4370	    if (c == 0) {
4371		c = E2A(' ');
4372	    } else if (c < E2A(' ')) {
4373		c = DECtoASCII(c);
4374	    } else if (c == 0x7f) {
4375		c = 0x5f;
4376	    }
4377	    *lp++ = CharOf(A2E(c));
4378	}
4379	if (c != E2A(' '))
4380	    result = lp;
4381    }
4382
4383    /*
4384     * If requested, trim trailing blanks from selected lines.  Do not do this
4385     * if the line is wrapped.
4386     */
4387    if (!*eol || !screen->trim_selection)
4388	result = lp;
4389
4390    return (result);
4391}
4392
4393/* 32 + following 7-bit word:
4394
4395   1:0  Button no: 0, 1, 2.  3=release.
4396     2  shift
4397     3  meta
4398     4  ctrl
4399     5  set for motion notify
4400     6  set for wheel
4401*/
4402
4403/* Position: 32 - 255. */
4404static int
4405BtnCode(XButtonEvent *event, int button)
4406{
4407    int result = (int) (32 + (KeyState(event->state) << 2));
4408
4409    if (event->type == MotionNotify)
4410	result += 32;
4411
4412    if (button < 0 || button > 5) {
4413	result += 3;
4414    } else {
4415	if (button > 3)
4416	    result += (64 - 4);
4417	result += button;
4418    }
4419    TRACE(("BtnCode button %d, %s state " FMT_MODIFIER_NAMES " ->%#x\n",
4420	   button,
4421	   visibleEventType(event->type),
4422	   ARG_MODIFIER_NAMES(event->state),
4423	   result));
4424    return result;
4425}
4426
4427static unsigned
4428EmitButtonCode(TScreen *screen,
4429	       Char *line,
4430	       unsigned count,
4431	       XButtonEvent *event,
4432	       int button)
4433{
4434    int value;
4435
4436    if (screen->send_mouse_pos == X10_MOUSE) {
4437	value = CharOf(' ' + button);
4438    } else {
4439	value = BtnCode(event, button);
4440    }
4441
4442    switch (screen->extend_coords) {
4443    default:
4444	line[count++] = CharOf(value);
4445	break;
4446    case SET_SGR_EXT_MODE_MOUSE:
4447	value -= 32;		/* encoding starts at zero */
4448	/* FALLTHRU */
4449    case SET_URXVT_EXT_MODE_MOUSE:
4450	count += (unsigned) sprintf((char *) line + count, "%d", value);
4451	break;
4452    case SET_EXT_MODE_MOUSE:
4453	if (value < 128) {
4454	    line[count++] = CharOf(value);
4455	} else {
4456	    line[count++] = CharOf(0xC0 + (value >> 6));
4457	    line[count++] = CharOf(0x80 + (value & 0x3F));
4458	}
4459	break;
4460    }
4461    return count;
4462}
4463
4464static int
4465FirstBitN(int bits)
4466{
4467    int result = -1;
4468    if (bits > 0) {
4469	result = 0;
4470	while (!(bits & 1)) {
4471	    bits /= 2;
4472	    ++result;
4473	}
4474    }
4475    return result;
4476}
4477
4478#define ButtonBit(button) ((button >= 0) ? (1 << (button)) : 0)
4479
4480#define EMIT_BUTTON(button) EmitButtonCode(screen, line, count, event, button)
4481
4482static void
4483EditorButton(XtermWidget xw, XButtonEvent *event)
4484{
4485    TScreen *screen = TScreenOf(xw);
4486    int pty = screen->respond;
4487    int mouse_limit = MouseLimit(screen);
4488    Char line[32];
4489    Char final = 'M';
4490    int row, col;
4491    int button;
4492    unsigned count = 0;
4493    Boolean changed = True;
4494
4495    /* If button event, get button # adjusted for DEC compatibility */
4496    button = (int) (event->button - 1);
4497    if (button >= 3)
4498	button++;
4499
4500    /* Compute character position of mouse pointer */
4501    row = (event->y - screen->border) / FontHeight(screen);
4502    col = (event->x - OriginX(screen)) / FontWidth(screen);
4503
4504    /* Limit to screen dimensions */
4505    if (row < 0)
4506	row = 0;
4507    else if (row > screen->max_row)
4508	row = screen->max_row;
4509
4510    if (col < 0)
4511	col = 0;
4512    else if (col > screen->max_col)
4513	col = screen->max_col;
4514
4515    if (mouse_limit > 0) {
4516	/* Limit to representable mouse dimensions */
4517	if (row > mouse_limit)
4518	    row = mouse_limit;
4519	if (col > mouse_limit)
4520	    col = mouse_limit;
4521    }
4522
4523    /* Build key sequence starting with \E[M */
4524    if (screen->control_eight_bits) {
4525	line[count++] = ANSI_CSI;
4526    } else {
4527	line[count++] = ANSI_ESC;
4528	line[count++] = '[';
4529    }
4530    switch (screen->extend_coords) {
4531    case 0:
4532    case SET_EXT_MODE_MOUSE:
4533#if OPT_SCO_FUNC_KEYS
4534	if (xw->keyboard.type == keyboardIsSCO) {
4535	    /*
4536	     * SCO function key F1 is \E[M, which would conflict with xterm's
4537	     * normal kmous.
4538	     */
4539	    line[count++] = '>';
4540	}
4541#endif
4542	line[count++] = final;
4543	break;
4544    case SET_SGR_EXT_MODE_MOUSE:
4545	line[count++] = '<';
4546	break;
4547    }
4548
4549    /* Add event code to key sequence */
4550    if (screen->send_mouse_pos == X10_MOUSE) {
4551	count = EMIT_BUTTON(button);
4552    } else {
4553	/* Button-Motion events */
4554	switch (event->type) {
4555	case ButtonPress:
4556	    screen->mouse_button |= ButtonBit(button);
4557	    count = EMIT_BUTTON(button);
4558	    break;
4559	case ButtonRelease:
4560	    /*
4561	     * Wheel mouse interface generates release-events for buttons
4562	     * 4 and 5, coded here as 3 and 4 respectively.  We change the
4563	     * release for buttons 1..3 to a -1, which will be later mapped
4564	     * into a "0" (some button was released).
4565	     */
4566	    screen->mouse_button &= ~ButtonBit(button);
4567	    if (button < 3) {
4568		switch (screen->extend_coords) {
4569		case SET_SGR_EXT_MODE_MOUSE:
4570		    final = 'm';
4571		    break;
4572		default:
4573		    button = -1;
4574		    break;
4575		}
4576	    }
4577	    count = EMIT_BUTTON(button);
4578	    break;
4579	case MotionNotify:
4580	    /* BTN_EVENT_MOUSE and ANY_EVENT_MOUSE modes send motion
4581	     * events only if character cell has changed.
4582	     */
4583	    if ((row == screen->mouse_row)
4584		&& (col == screen->mouse_col)) {
4585		changed = False;
4586	    } else {
4587		count = EMIT_BUTTON(FirstBitN(screen->mouse_button));
4588	    }
4589	    break;
4590	default:
4591	    changed = False;
4592	    break;
4593	}
4594    }
4595
4596    if (changed) {
4597	screen->mouse_row = row;
4598	screen->mouse_col = col;
4599
4600	TRACE(("mouse at %d,%d button+mask = %#x\n", row, col, line[count - 1]));
4601
4602	/* Add pointer position to key sequence */
4603	count = EmitMousePositionSeparator(screen, line, count);
4604	count = EmitMousePosition(screen, line, count, col);
4605	count = EmitMousePositionSeparator(screen, line, count);
4606	count = EmitMousePosition(screen, line, count, row);
4607
4608	switch (screen->extend_coords) {
4609	case SET_SGR_EXT_MODE_MOUSE:
4610	case SET_URXVT_EXT_MODE_MOUSE:
4611	    line[count++] = final;
4612	    break;
4613	}
4614
4615	/* Transmit key sequence to process running under xterm */
4616	v_write(pty, line, count);
4617    }
4618    return;
4619}
4620
4621#if OPT_FOCUS_EVENT
4622void
4623SendFocusButton(XtermWidget xw, XFocusChangeEvent *event)
4624{
4625    TScreen *screen = TScreenOf(xw);
4626
4627    if (screen->send_focus_pos) {
4628	ANSI reply;
4629
4630	memset(&reply, 0, sizeof(reply));
4631	reply.a_type = ANSI_CSI;
4632
4633#if OPT_SCO_FUNC_KEYS
4634	if (xw->keyboard.type == keyboardIsSCO) {
4635	    reply.a_pintro = '>';
4636	}
4637#endif
4638	reply.a_final = CharOf((event->type == FocusIn) ? 'I' : 'O');
4639	unparseseq(xw, &reply);
4640    }
4641    return;
4642}
4643#endif /* OPT_FOCUS_EVENT */
4644
4645#if OPT_SELECTION_OPS
4646/*
4647 * Get the event-time, needed to process selections.
4648 */
4649static Time
4650getEventTime(XEvent *event)
4651{
4652    Time result;
4653
4654    if (IsBtnEvent(event)) {
4655	result = ((XButtonEvent *) event)->time;
4656    } else if (IsKeyEvent(event)) {
4657	result = ((XKeyEvent *) event)->time;
4658    } else {
4659	result = 0;
4660    }
4661
4662    return result;
4663}
4664
4665/* obtain the selection string, passing the endpoints to caller's parameters */
4666static void
4667doSelectionFormat(XtermWidget xw,
4668		  Widget w,
4669		  XEvent *event,
4670		  String *params,
4671		  Cardinal *num_params,
4672		  FormatSelect format_select)
4673{
4674    TScreen *screen = TScreenOf(xw);
4675    InternalSelect *mydata = &(screen->internal_select);
4676
4677    memset(mydata, 0, sizeof(*mydata));
4678    mydata->format = x_strdup(params[0]);
4679    mydata->format_select = format_select;
4680
4681    /* override flags so that SelectionReceived only updates a buffer */
4682#if OPT_PASTE64
4683    mydata->base64_paste = screen->base64_paste;
4684    screen->base64_paste = 0;
4685#endif
4686#if OPT_READLINE
4687    mydata->paste_brackets = screen->paste_brackets;
4688    SCREEN_FLAG_unset(screen, paste_brackets);
4689#endif
4690
4691    screen->selectToBuffer = True;
4692    xtermGetSelection(w, getEventTime(event), params + 1, *num_params - 1, NULL);
4693}
4694
4695/* obtain data from the screen, passing the endpoints to caller's parameters */
4696static char *
4697getDataFromScreen(XtermWidget xw, String method, CELL *start, CELL *finish)
4698{
4699    TScreen *screen = TScreenOf(xw);
4700
4701    CELL save_old_start = screen->startH;
4702    CELL save_old_end = screen->endH;
4703
4704    CELL save_startSel = screen->startSel;
4705    CELL save_startRaw = screen->startRaw;
4706    CELL save_finishSel = screen->endSel;
4707    CELL save_finishRaw = screen->endRaw;
4708
4709    int save_firstValidRow = screen->firstValidRow;
4710    int save_lastValidRow = screen->lastValidRow;
4711
4712    const Cardinal noClick = 0;
4713    int save_numberOfClicks = screen->numberOfClicks;
4714
4715    SelectUnit saveUnits = screen->selectUnit;
4716    SelectUnit saveMap = screen->selectMap[noClick];
4717#if OPT_SELECT_REGEX
4718    char *saveExpr = screen->selectExpr[noClick];
4719#endif
4720
4721    Char *save_selection_data = screen->selection_data;
4722    int save_selection_size = screen->selection_size;
4723    unsigned long save_selection_length = screen->selection_length;
4724
4725    char *result = 0;
4726
4727    TRACE(("getDataFromScreen %s\n", method));
4728
4729    screen->selection_data = 0;
4730    screen->selection_size = 0;
4731    screen->selection_length = 0;
4732
4733    screen->numberOfClicks = 1;
4734    lookupSelectUnit(xw, noClick, method);
4735    screen->selectUnit = screen->selectMap[noClick];
4736
4737    memset(start, 0, sizeof(*start));
4738    start->row = screen->cur_row;
4739    start->col = screen->cur_col;
4740    finish->row = screen->cur_row;
4741    finish->col = screen->max_col;
4742
4743    ComputeSelect(xw, start, finish, False);
4744    SaltTextAway(xw, &(screen->startSel), &(screen->endSel));
4745
4746    if (screen->selection_length && screen->selection_data) {
4747	TRACE(("...getDataFromScreen selection_data %.*s\n",
4748	       (int) screen->selection_length,
4749	       screen->selection_data));
4750	result = malloc(screen->selection_length + 1);
4751	if (result) {
4752	    memcpy(result, screen->selection_data, screen->selection_length);
4753	    result[screen->selection_length] = 0;
4754	}
4755	free(screen->selection_data);
4756    }
4757
4758    TRACE(("...getDataFromScreen restoring previous selection\n"));
4759
4760    screen->startSel = save_startSel;
4761    screen->startRaw = save_startRaw;
4762    screen->endSel = save_finishSel;
4763    screen->endRaw = save_finishRaw;
4764
4765    screen->firstValidRow = save_firstValidRow;
4766    screen->lastValidRow = save_lastValidRow;
4767
4768    screen->numberOfClicks = save_numberOfClicks;
4769    screen->selectUnit = saveUnits;
4770    screen->selectMap[noClick] = saveMap;
4771#if OPT_SELECT_REGEX
4772    screen->selectExpr[noClick] = saveExpr;
4773#endif
4774
4775    screen->selection_data = save_selection_data;
4776    screen->selection_size = save_selection_size;
4777    screen->selection_length = save_selection_length;
4778
4779    TrackText(xw, &save_old_start, &save_old_end);
4780
4781    TRACE(("...getDataFromScreen done\n"));
4782    return result;
4783}
4784
4785/*
4786 * Split-up the format before substituting data, to avoid quoting issues.
4787 * The resource mechanism has a limited ability to handle escapes.  We take
4788 * the result as if it were an sh-type string and parse it into a regular
4789 * argv array.
4790 */
4791static char **
4792tokenizeFormat(String format)
4793{
4794    char **result = 0;
4795    int pass;
4796    int argc;
4797    int n;
4798
4799    format = x_skip_blanks(format);
4800    if (*format != '\0') {
4801	char *blob = x_strdup(format);
4802
4803	for (pass = 0; pass < 2; ++pass) {
4804	    int used = 0;
4805	    int first = 1;
4806	    int escaped = 0;
4807	    int squoted = 0;
4808	    int dquoted = 0;
4809
4810	    argc = 0;
4811	    for (n = 0; format[n] != '\0'; ++n) {
4812		if (escaped) {
4813		    blob[used++] = format[n];
4814		    escaped = 0;
4815		} else if (format[n] == '"') {
4816		    if (!squoted) {
4817			if (!dquoted)
4818			    blob[used++] = format[n];
4819			dquoted = !dquoted;
4820		    }
4821		} else if (format[n] == '\'') {
4822		    if (!dquoted) {
4823			if (!squoted)
4824			    blob[used++] = format[n];
4825			squoted = !squoted;
4826		    }
4827		} else if (format[n] == '\\') {
4828		    blob[used++] = format[n];
4829		    escaped = 1;
4830		} else {
4831		    if (first) {
4832			first = 0;
4833			if (pass) {
4834			    result[argc] = &blob[n];
4835			}
4836			++argc;
4837		    }
4838		    if (isspace((Char) format[n])) {
4839			first = !isspace((Char) format[n + 1]);
4840			if (squoted || dquoted) {
4841			    blob[used++] = format[n];
4842			} else if (first) {
4843			    blob[used++] = '\0';
4844			}
4845		    } else {
4846			blob[used++] = format[n];
4847		    }
4848		}
4849	    }
4850	    blob[used] = '\0';
4851	    assert(strlen(blob) <= strlen(format));
4852	    if (!pass) {
4853		result = TypeCallocN(char *, argc + 1);
4854		if (result == 0) {
4855		    free(blob);
4856		    break;
4857		}
4858	    }
4859	}
4860    }
4861#if OPT_TRACE
4862    if (result) {
4863	TRACE(("tokenizeFormat %s\n", format));
4864	for (argc = 0; result[argc]; ++argc) {
4865	    TRACE(("argv[%d] = %s\n", argc, result[argc]));
4866	}
4867    }
4868#endif
4869
4870    return result;
4871}
4872
4873static void
4874formatVideoAttrs(XtermWidget xw, char *buffer, CELL *cell)
4875{
4876    TScreen *screen = TScreenOf(xw);
4877    LineData *ld = GET_LINEDATA(screen, cell->row);
4878
4879    *buffer = '\0';
4880    if (ld != 0 && cell->col < (int) ld->lineSize) {
4881	IAttr attribs = ld->attribs[cell->col];
4882	const char *delim = "";
4883
4884	if (attribs & INVERSE) {
4885	    buffer += sprintf(buffer, "7");
4886	    delim = ";";
4887	}
4888	if (attribs & UNDERLINE) {
4889	    buffer += sprintf(buffer, "%s4", delim);
4890	    delim = ";";
4891	}
4892	if (attribs & BOLD) {
4893	    buffer += sprintf(buffer, "%s1", delim);
4894	    delim = ";";
4895	}
4896	if (attribs & BLINK) {
4897	    buffer += sprintf(buffer, "%s5", delim);
4898	    delim = ";";
4899	}
4900#if OPT_ISO_COLORS
4901	if (attribs & FG_COLOR) {
4902	    unsigned fg = extract_fg(xw, ld->color[cell->col], attribs);
4903	    if (fg < 8) {
4904		fg += 30;
4905	    } else if (fg < 16) {
4906		fg += 90;
4907	    } else {
4908		buffer += sprintf(buffer, "%s38;5", delim);
4909		delim = ";";
4910	    }
4911	    buffer += sprintf(buffer, "%s%u", delim, fg);
4912	    delim = ";";
4913	}
4914	if (attribs & BG_COLOR) {
4915	    unsigned bg = extract_bg(xw, ld->color[cell->col], attribs);
4916	    if (bg < 8) {
4917		bg += 40;
4918	    } else if (bg < 16) {
4919		bg += 100;
4920	    } else {
4921		buffer += sprintf(buffer, "%s48;5", delim);
4922		delim = ";";
4923	    }
4924	    (void) sprintf(buffer, "%s%u", delim, bg);
4925	}
4926#endif
4927    }
4928}
4929
4930/* substitute data into format, reallocating the result */
4931static char *
4932expandFormat(XtermWidget xw,
4933	     const char *format,
4934	     char *data,
4935	     CELL *start,
4936	     CELL *finish)
4937{
4938    char *result = 0;
4939    if (!IsEmpty(format)) {
4940	static char empty[1];
4941	int pass;
4942	int n;
4943	char numbers[80];
4944
4945	if (data == 0)
4946	    data = empty;
4947
4948	for (pass = 0; pass < 2; ++pass) {
4949	    size_t need = 0;
4950
4951	    for (n = 0; format[n] != '\0'; ++n) {
4952		char *value = 0;
4953
4954		if (format[n] == '%') {
4955		    switch (format[++n]) {
4956		    case '%':
4957			if (pass) {
4958			    result[need] = format[n];
4959			}
4960			++need;
4961			break;
4962		    case 'P':
4963			sprintf(numbers, "%d;%d",
4964				TScreenOf(xw)->topline + start->row + 1,
4965				start->col + 1);
4966			value = numbers;
4967			break;
4968		    case 'p':
4969			sprintf(numbers, "%d;%d",
4970				TScreenOf(xw)->topline + finish->row + 1,
4971				finish->col + 1);
4972			value = numbers;
4973			break;
4974		    case 'S':
4975			sprintf(numbers, "%u", (unsigned) strlen(data));
4976			value = numbers;
4977			break;
4978		    case 's':
4979			value = data;
4980			break;
4981		    case 'T':
4982			if ((value = x_strtrim(data)) != 0) {
4983			    sprintf(numbers, "%u", (unsigned) strlen(value));
4984			    free(value);
4985			} else {
4986			    strcpy(numbers, "0");
4987			}
4988			value = numbers;
4989			break;
4990		    case 't':
4991			value = x_strtrim(data);
4992			break;
4993		    case 'V':
4994			formatVideoAttrs(xw, numbers, start);
4995			value = numbers;
4996			break;
4997		    case 'v':
4998			formatVideoAttrs(xw, numbers, finish);
4999			value = numbers;
5000			break;
5001		    default:
5002			if (pass) {
5003			    result[need] = format[n];
5004			}
5005			--n;
5006			++need;
5007			break;
5008		    }
5009		    if (value != 0) {
5010			if (pass) {
5011			    strcpy(result + need, value);
5012			}
5013			need += strlen(value);
5014			if (value != numbers && value != data) {
5015			    free(value);
5016			}
5017		    }
5018		} else {
5019		    if (pass) {
5020			result[need] = format[n];
5021		    }
5022		    ++need;
5023		}
5024	    }
5025	    if (pass) {
5026		result[need] = '\0';
5027	    } else {
5028		++need;
5029		result = malloc(need);
5030		if (result == 0) {
5031		    break;
5032		}
5033	    }
5034	}
5035    }
5036    TRACE(("expandFormat(%s) = %s\n", NonNull(format), NonNull(result)));
5037    return result;
5038}
5039
5040/* execute the command after forking.  The main process frees its data */
5041static void
5042executeCommand(char **argv)
5043{
5044    if (argv != 0 && argv[0] != 0) {
5045	if (fork() == 0) {
5046	    execvp(argv[0], argv);
5047	    exit(EXIT_FAILURE);
5048	}
5049    }
5050}
5051
5052static void
5053freeArgv(char *blob, char **argv)
5054{
5055    int n;
5056
5057    if (blob) {
5058	free(blob);
5059	if (argv) {
5060	    for (n = 0; argv[n]; ++n)
5061		free(argv[n]);
5062	    free(argv);
5063	}
5064    }
5065}
5066
5067static void
5068reallyExecFormatted(Widget w, char *format, char *data, CELL *start, CELL *finish)
5069{
5070    XtermWidget xw;
5071
5072    if ((xw = getXtermWidget(w)) != 0) {
5073	char **argv;
5074	char *blob;
5075	int argc;
5076
5077	if ((argv = tokenizeFormat(format)) != 0) {
5078	    blob = argv[0];
5079	    for (argc = 0; argv[argc] != 0; ++argc) {
5080		argv[argc] = expandFormat(xw, argv[argc], data, start, finish);
5081	    }
5082	    executeCommand(argv);
5083	    freeArgv(blob, argv);
5084	}
5085    }
5086}
5087
5088void
5089HandleExecFormatted(Widget w,
5090		    XEvent *event,
5091		    String *params,	/* selections */
5092		    Cardinal *num_params)
5093{
5094    XtermWidget xw;
5095
5096    TRACE(("HandleExecFormatted(%d)\n", *num_params));
5097    if ((xw = getXtermWidget(w)) != 0 &&
5098	(*num_params > 1)) {
5099	doSelectionFormat(xw, w, event, params, num_params, reallyExecFormatted);
5100    }
5101}
5102
5103void
5104HandleExecSelectable(Widget w,
5105		     XEvent *event GCC_UNUSED,
5106		     String *params,	/* selections */
5107		     Cardinal *num_params)
5108{
5109    XtermWidget xw;
5110
5111    if ((xw = getXtermWidget(w)) != 0) {
5112	TRACE(("HandleExecSelectable(%d)\n", *num_params));
5113
5114	if (*num_params == 2) {
5115	    CELL start, finish;
5116	    char *data;
5117	    char **argv;
5118	    char *blob;
5119	    int argc;
5120
5121	    data = getDataFromScreen(xw, params[1], &start, &finish);
5122	    if (data != 0) {
5123		if ((argv = tokenizeFormat(params[0])) != 0) {
5124		    blob = argv[0];
5125		    for (argc = 0; argv[argc] != 0; ++argc) {
5126			argv[argc] = expandFormat(xw, argv[argc], data,
5127						  &start, &finish);
5128		    }
5129		    executeCommand(argv);
5130		    freeArgv(blob, argv);
5131		}
5132		free(data);
5133	    }
5134	}
5135    }
5136}
5137
5138static void
5139reallyInsertFormatted(Widget w, char *format, char *data, CELL *start, CELL *finish)
5140{
5141    XtermWidget xw;
5142
5143    if ((xw = getXtermWidget(w)) != 0) {
5144	char *exps;
5145
5146	if ((exps = expandFormat(xw, format, data, start, finish)) != 0) {
5147	    unparseputs(xw, exps);
5148	    unparse_end(xw);
5149	    free(exps);
5150	}
5151    }
5152}
5153
5154void
5155HandleInsertFormatted(Widget w,
5156		      XEvent *event,
5157		      String *params,	/* selections */
5158		      Cardinal *num_params)
5159{
5160    XtermWidget xw;
5161
5162    TRACE(("HandleInsertFormatted(%d)\n", *num_params));
5163    if ((xw = getXtermWidget(w)) != 0 &&
5164	(*num_params > 1)) {
5165	doSelectionFormat(xw, w, event, params, num_params, reallyInsertFormatted);
5166    }
5167}
5168
5169void
5170HandleInsertSelectable(Widget w,
5171		       XEvent *event GCC_UNUSED,
5172		       String *params,	/* selections */
5173		       Cardinal *num_params)
5174{
5175    XtermWidget xw;
5176
5177    if ((xw = getXtermWidget(w)) != 0) {
5178	TRACE(("HandleInsertSelectable(%d)\n", *num_params));
5179
5180	if (*num_params == 2) {
5181	    CELL start, finish;
5182	    char *data;
5183	    char *temp = x_strdup(params[0]);
5184	    char *exps;
5185
5186	    data = getDataFromScreen(xw, params[1], &start, &finish);
5187	    if (data != 0) {
5188		exps = expandFormat(xw, temp, data, &start, &finish);
5189		if (exps != 0) {
5190		    unparseputs(xw, exps);
5191		    unparse_end(xw);
5192		    free(exps);
5193		}
5194		free(data);
5195	    }
5196	    free(temp);
5197	}
5198    }
5199}
5200#endif /* OPT_SELECTION_OPS */
5201