button.c revision 913cc679
1913cc679Smrg/* $XTermId: button.c,v 1.524 2017/05/30 08:58:29 tom Exp $ */
2d522f475Smrg
3d522f475Smrg/*
4913cc679Smrg * Copyright 1999-2016,2017 by Thomas E. Dickey
5d522f475Smrg *
6d522f475Smrg *                         All Rights Reserved
7d522f475Smrg *
8d522f475Smrg * Permission is hereby granted, free of charge, to any person obtaining a
9d522f475Smrg * copy of this software and associated documentation files (the
10d522f475Smrg * "Software"), to deal in the Software without restriction, including
11d522f475Smrg * without limitation the rights to use, copy, modify, merge, publish,
12d522f475Smrg * distribute, sublicense, and/or sell copies of the Software, and to
13d522f475Smrg * permit persons to whom the Software is furnished to do so, subject to
14d522f475Smrg * the following conditions:
15d522f475Smrg *
16d522f475Smrg * The above copyright notice and this permission notice shall be included
17d522f475Smrg * in all copies or substantial portions of the Software.
18d522f475Smrg *
19d522f475Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20d522f475Smrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21d522f475Smrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22d522f475Smrg * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
23d522f475Smrg * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24d522f475Smrg * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25d522f475Smrg * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26d522f475Smrg *
27d522f475Smrg * Except as contained in this notice, the name(s) of the above copyright
28d522f475Smrg * holders shall not be used in advertising or otherwise to promote the
29d522f475Smrg * sale, use or other dealings in this Software without prior written
30d522f475Smrg * authorization.
31d522f475Smrg *
32d522f475Smrg *
33d522f475Smrg * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
34d522f475Smrg *
35d522f475Smrg *                         All Rights Reserved
36d522f475Smrg *
37d522f475Smrg * Permission to use, copy, modify, and distribute this software and its
38d522f475Smrg * documentation for any purpose and without fee is hereby granted,
39d522f475Smrg * provided that the above copyright notice appear in all copies and that
40d522f475Smrg * both that copyright notice and this permission notice appear in
41d522f475Smrg * supporting documentation, and that the name of Digital Equipment
42d522f475Smrg * Corporation not be used in advertising or publicity pertaining to
43d522f475Smrg * distribution of the software without specific, written prior permission.
44d522f475Smrg *
45d522f475Smrg *
46d522f475Smrg * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
47d522f475Smrg * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
48d522f475Smrg * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
49d522f475Smrg * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
50d522f475Smrg * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
51d522f475Smrg * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
52d522f475Smrg * SOFTWARE.
53d522f475Smrg */
54d522f475Smrg
55d522f475Smrg/*
56d522f475Smrgbutton.c	Handles button events in the terminal emulator.
57d522f475Smrg		does cut/paste operations, change modes via menu,
58d522f475Smrg		passes button events through to some applications.
59d522f475Smrg				J. Gettys.
60d522f475Smrg*/
61d522f475Smrg
62d522f475Smrg#include <xterm.h>
63d522f475Smrg
64d522f475Smrg#include <stdio.h>
650bd37d32Smrg#include <ctype.h>
6620d2c4d2Smrg#include <assert.h>
67d522f475Smrg
68d522f475Smrg#include <X11/Xatom.h>
69d522f475Smrg#include <X11/Xmu/Atoms.h>
70d522f475Smrg#include <X11/Xmu/StdSel.h>
71d522f475Smrg
72d522f475Smrg#include <xutf8.h>
73d522f475Smrg#include <fontutils.h>
74d522f475Smrg
75d522f475Smrg#include <data.h>
76d522f475Smrg#include <error.h>
77d522f475Smrg#include <menu.h>
78d522f475Smrg#include <charclass.h>
79d522f475Smrg#include <xstrings.h>
80d522f475Smrg
81d522f475Smrg#if OPT_SELECT_REGEX
82d522f475Smrg#ifdef HAVE_PCREPOSIX_H
83d522f475Smrg#include <pcreposix.h>
84d522f475Smrg#else /* POSIX regex.h */
85d522f475Smrg#include <sys/types.h>
86d522f475Smrg#include <regex.h>
87d522f475Smrg#endif
88d522f475Smrg#endif
89d522f475Smrg
90d522f475Smrg#if OPT_WIDE_CHARS
91d522f475Smrg#include <ctype.h>
92d522f475Smrg#include <wcwidth.h>
93d522f475Smrg#else
94d522f475Smrg#define CharacterClass(value) \
95913cc679Smrg	charClass[(value) & ((sizeof(charClass)/sizeof(charClass[0]))-1)]
96d522f475Smrg#endif
97d522f475Smrg
98956cc18dSsnj    /*
99956cc18dSsnj     * We'll generally map rows to indices when doing selection.
100956cc18dSsnj     * Simplify that with a macro.
101956cc18dSsnj     *
102956cc18dSsnj     * Note that ROW2INX() is safe to use with auto increment/decrement for
103956cc18dSsnj     * the row expression since that is evaluated once.
104956cc18dSsnj     */
105956cc18dSsnj#define GET_LINEDATA(screen, row) \
106956cc18dSsnj	getLineData(screen, ROW2INX(screen, row))
107956cc18dSsnj
108956cc18dSsnj    /*
109956cc18dSsnj     * We reserve shift modifier for cut/paste operations.  In principle we
110956cc18dSsnj     * can pass through control and meta modifiers, but in practice, the
111956cc18dSsnj     * popup menu uses control, and the window manager is likely to use meta,
112956cc18dSsnj     * so those events are not delivered to SendMousePosition.
113956cc18dSsnj     */
114d522f475Smrg#define OurModifiers (ShiftMask | ControlMask | Mod1Mask)
115d522f475Smrg#define AllModifiers (ShiftMask | LockMask | ControlMask | Mod1Mask | \
116d522f475Smrg		      Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask)
117d522f475Smrg
118492d43a5Smrg#define BtnModifiers(event) (event->state & OurModifiers)
119492d43a5Smrg#define KeyModifiers(event) (event->xbutton.state & OurModifiers)
120492d43a5Smrg
121492d43a5Smrg#define IsBtnEvent(event) ((event)->type == ButtonPress || (event)->type == ButtonRelease)
1220bd37d32Smrg#define IsKeyEvent(event) ((event)->type == KeyPress    || (event)->type == KeyRelease)
123d522f475Smrg
1242eaa94a1Schristos#define KeyState(x) (((int) ((x) & (ShiftMask|ControlMask))) \
1252eaa94a1Schristos			  + (((x) & Mod1Mask) ? 2 : 0))
126d522f475Smrg    /* adds together the bits:
127d522f475Smrg       shift key -> 1
128d522f475Smrg       meta key  -> 2
129d522f475Smrg       control key -> 4 */
130d522f475Smrg
131d522f475Smrg#define	Coordinate(s,c)	((c)->row * MaxCols(s) + (c)->col)
132d522f475Smrg
133d522f475Smrgstatic const CELL zeroCELL =
134d522f475Smrg{0, 0};
135d522f475Smrg
136d522f475Smrg#if OPT_DEC_LOCATOR
137894e0ac8Smrgstatic Bool SendLocatorPosition(XtermWidget xw, XButtonEvent *event);
138894e0ac8Smrgstatic void CheckLocatorPosition(XtermWidget xw, XButtonEvent *event);
139d522f475Smrg#endif /* OPT_DEC_LOCATOR */
140d522f475Smrg
141d522f475Smrg/* Multi-click handling */
142d522f475Smrg#if OPT_READLINE
143d522f475Smrgstatic Time lastButtonDownTime = 0;
144d522f475Smrgstatic int ExtendingSelection = 0;
145d522f475Smrgstatic Time lastButton3UpTime = 0;
146d522f475Smrgstatic Time lastButton3DoubleDownTime = 0;
147d522f475Smrgstatic CELL lastButton3;	/* At the release time */
148d522f475Smrg#endif /* OPT_READLINE */
149d522f475Smrg
150e0a2b6dfSmrgstatic Char *SaveText(TScreen *screen, int row, int scol, int ecol,
151e0a2b6dfSmrg		      Char *lp, int *eol);
152e0a2b6dfSmrgstatic int Length(TScreen *screen, int row, int scol, int ecol);
153e0a2b6dfSmrgstatic void ComputeSelect(XtermWidget xw, CELL *startc, CELL *endc, Bool extend);
154894e0ac8Smrgstatic void EditorButton(XtermWidget xw, XButtonEvent *event);
155894e0ac8Smrgstatic void EndExtend(XtermWidget w, XEvent *event, String *params, Cardinal
156d522f475Smrg		      num_params, Bool use_cursor_loc);
157e0a2b6dfSmrgstatic void ExtendExtend(XtermWidget xw, const CELL *cell);
158e0a2b6dfSmrgstatic void PointToCELL(TScreen *screen, int y, int x, CELL *cell);
159e0a2b6dfSmrgstatic void ReHiliteText(XtermWidget xw, CELL *first, CELL *last);
160e0a2b6dfSmrgstatic void SaltTextAway(XtermWidget xw, CELL *cellc, CELL *cell);
161894e0ac8Smrgstatic void SelectSet(XtermWidget xw, XEvent *event, String *params, Cardinal num_params);
162d522f475Smrgstatic void SelectionReceived PROTO_XT_SEL_CB_ARGS;
163e0a2b6dfSmrgstatic void StartSelect(XtermWidget xw, const CELL *cell);
164894e0ac8Smrgstatic void TrackDown(XtermWidget xw, XButtonEvent *event);
165e0a2b6dfSmrgstatic void TrackText(XtermWidget xw, const CELL *first, const CELL *last);
166e0a2b6dfSmrgstatic void _OwnSelection(XtermWidget xw, String *selections, Cardinal count);
167894e0ac8Smrgstatic void do_select_end(XtermWidget xw, XEvent *event, String *params,
168d522f475Smrg			  Cardinal *num_params, Bool use_cursor_loc);
169d522f475Smrg
170492d43a5Smrg#define MOUSE_LIMIT (255 - 32)
171d522f475Smrg
172492d43a5Smrg/* Send SET_EXT_SIZE_MOUSE to enable offsets up to EXT_MOUSE_LIMIT */
173492d43a5Smrg#define EXT_MOUSE_LIMIT (2047 - 32)
174492d43a5Smrg#define EXT_MOUSE_START (127 - 32)
175d522f475Smrg
1760bd37d32Smrgstatic int
177e0a2b6dfSmrgMouseLimit(TScreen *screen)
1780bd37d32Smrg{
1790bd37d32Smrg    int mouse_limit;
1800bd37d32Smrg
1810bd37d32Smrg    switch (screen->extend_coords) {
1820bd37d32Smrg    default:
1830bd37d32Smrg	mouse_limit = MOUSE_LIMIT;
1840bd37d32Smrg	break;
1850bd37d32Smrg    case SET_EXT_MODE_MOUSE:
1860bd37d32Smrg	mouse_limit = EXT_MOUSE_LIMIT;
1870bd37d32Smrg	break;
1880bd37d32Smrg    case SET_SGR_EXT_MODE_MOUSE:
1890bd37d32Smrg    case SET_URXVT_EXT_MODE_MOUSE:
1900bd37d32Smrg	mouse_limit = -1;
1910bd37d32Smrg	break;
1920bd37d32Smrg    }
1930bd37d32Smrg    return mouse_limit;
1940bd37d32Smrg}
1950bd37d32Smrg
196492d43a5Smrgstatic unsigned
197e0a2b6dfSmrgEmitMousePosition(TScreen *screen, Char line[], unsigned count, int value)
198492d43a5Smrg{
1990bd37d32Smrg    int mouse_limit = MouseLimit(screen);
200492d43a5Smrg
201492d43a5Smrg    /*
202492d43a5Smrg     * Add pointer position to key sequence
203492d43a5Smrg     *
204492d43a5Smrg     * In extended mode we encode large positions as two-byte UTF-8.
205492d43a5Smrg     *
206492d43a5Smrg     * NOTE: historically, it was possible to emit 256, which became
207492d43a5Smrg     * zero by truncation to 8 bits. While this was arguably a bug,
208492d43a5Smrg     * it's also somewhat useful as a past-end marker. We preserve
209492d43a5Smrg     * this behavior for both normal and extended mouse modes.
210492d43a5Smrg     */
2110bd37d32Smrg    switch (screen->extend_coords) {
2120bd37d32Smrg    default:
2130bd37d32Smrg	if (value == mouse_limit) {
2140bd37d32Smrg	    line[count++] = CharOf(0);
2150bd37d32Smrg	} else {
2160bd37d32Smrg	    line[count++] = CharOf(' ' + value + 1);
2170bd37d32Smrg	}
2180bd37d32Smrg	break;
2190bd37d32Smrg    case SET_EXT_MODE_MOUSE:
2200bd37d32Smrg	if (value == mouse_limit) {
2210bd37d32Smrg	    line[count++] = CharOf(0);
2220bd37d32Smrg	} else if (value < EXT_MOUSE_START) {
2230bd37d32Smrg	    line[count++] = CharOf(' ' + value + 1);
2240bd37d32Smrg	} else {
2250bd37d32Smrg	    value += ' ' + 1;
2260bd37d32Smrg	    line[count++] = CharOf(0xC0 + (value >> 6));
2270bd37d32Smrg	    line[count++] = CharOf(0x80 + (value & 0x3F));
2280bd37d32Smrg	}
2290bd37d32Smrg	break;
2300bd37d32Smrg    case SET_SGR_EXT_MODE_MOUSE:
2310bd37d32Smrg	/* FALLTHRU */
2320bd37d32Smrg    case SET_URXVT_EXT_MODE_MOUSE:
2330bd37d32Smrg	count += (unsigned) sprintf((char *) line + count, "%d", value + 1);
2340bd37d32Smrg	break;
2350bd37d32Smrg    }
2360bd37d32Smrg    return count;
2370bd37d32Smrg}
2380bd37d32Smrg
2390bd37d32Smrgstatic unsigned
240e0a2b6dfSmrgEmitMousePositionSeparator(TScreen *screen, Char line[], unsigned count)
2410bd37d32Smrg{
2420bd37d32Smrg    switch (screen->extend_coords) {
2430bd37d32Smrg    case SET_SGR_EXT_MODE_MOUSE:
2440bd37d32Smrg    case SET_URXVT_EXT_MODE_MOUSE:
2450bd37d32Smrg	line[count++] = ';';
2460bd37d32Smrg	break;
247d522f475Smrg    }
248492d43a5Smrg    return count;
249492d43a5Smrg}
250d522f475Smrg
251492d43a5SmrgBool
252894e0ac8SmrgSendMousePosition(XtermWidget xw, XEvent *event)
253492d43a5Smrg{
254492d43a5Smrg    XButtonEvent *my_event = (XButtonEvent *) event;
255492d43a5Smrg    Bool result = False;
256d522f475Smrg
257913cc679Smrg    switch (okSendMousePos(xw)) {
258492d43a5Smrg    case MOUSE_OFF:
259492d43a5Smrg	/* If send_mouse_pos mode isn't on, we shouldn't be here */
260492d43a5Smrg	break;
261d522f475Smrg
262d522f475Smrg    case BTN_EVENT_MOUSE:
263d522f475Smrg    case ANY_EVENT_MOUSE:
264492d43a5Smrg	if (KeyModifiers(event) == 0 || KeyModifiers(event) == ControlMask) {
265492d43a5Smrg	    /* xterm extension for motion reporting. June 1998 */
266492d43a5Smrg	    /* EditorButton() will distinguish between the modes */
267492d43a5Smrg	    switch (event->type) {
268492d43a5Smrg	    case MotionNotify:
269492d43a5Smrg		my_event->button = 0;
270492d43a5Smrg		/* FALLTHRU */
271492d43a5Smrg	    case ButtonPress:
272492d43a5Smrg		/* FALLTHRU */
273492d43a5Smrg	    case ButtonRelease:
274492d43a5Smrg		EditorButton(xw, my_event);
275492d43a5Smrg		result = True;
276492d43a5Smrg		break;
2772eaa94a1Schristos	    }
278d522f475Smrg	}
279492d43a5Smrg	break;
280d522f475Smrg
281913cc679Smrg    case X10_MOUSE:		/* X10 compatibility sequences */
282492d43a5Smrg	if (IsBtnEvent(event)) {
283913cc679Smrg	    if (BtnModifiers(my_event) == 0) {
284913cc679Smrg		if (my_event->type == ButtonPress)
285492d43a5Smrg		    EditorButton(xw, my_event);
286913cc679Smrg		result = True;
287913cc679Smrg	    }
288913cc679Smrg	}
289913cc679Smrg	break;
290492d43a5Smrg
291913cc679Smrg    case VT200_HIGHLIGHT_MOUSE:	/* DEC vt200 hilite tracking */
292913cc679Smrg	if (IsBtnEvent(event)) {
293913cc679Smrg	    if (my_event->type == ButtonPress &&
294913cc679Smrg		BtnModifiers(my_event) == 0 &&
295913cc679Smrg		my_event->button == Button1) {
296913cc679Smrg		TrackDown(xw, my_event);
297913cc679Smrg		result = True;
298913cc679Smrg	    } else if (BtnModifiers(my_event) == 0
299913cc679Smrg		       || BtnModifiers(my_event) == ControlMask) {
300913cc679Smrg		EditorButton(xw, my_event);
301913cc679Smrg		result = True;
302913cc679Smrg	    }
303913cc679Smrg	}
304913cc679Smrg	break;
305492d43a5Smrg
306913cc679Smrg    case VT200_MOUSE:		/* DEC vt200 compatible */
307913cc679Smrg	if (IsBtnEvent(event)) {
308913cc679Smrg	    if (BtnModifiers(my_event) == 0
309913cc679Smrg		|| BtnModifiers(my_event) == ControlMask) {
310913cc679Smrg		EditorButton(xw, my_event);
311913cc679Smrg		result = True;
312913cc679Smrg	    }
313913cc679Smrg	}
314913cc679Smrg	break;
315913cc679Smrg
316913cc679Smrg    case DEC_LOCATOR:
317913cc679Smrg	if (IsBtnEvent(event)) {
318492d43a5Smrg#if OPT_DEC_LOCATOR
319913cc679Smrg	    result = SendLocatorPosition(xw, my_event);
320492d43a5Smrg#endif /* OPT_DEC_LOCATOR */
321492d43a5Smrg	}
322913cc679Smrg	break;
323d522f475Smrg    }
324492d43a5Smrg    return result;
325d522f475Smrg}
326d522f475Smrg
327d522f475Smrg#if OPT_DEC_LOCATOR
328d522f475Smrg
329d522f475Smrg#define	LocatorCoords( row, col, x, y, oor )			\
330d522f475Smrg    if( screen->locator_pixels ) {				\
331d522f475Smrg	(oor)=False; (row) = (y)+1; (col) = (x)+1;		\
332d522f475Smrg	/* Limit to screen dimensions */			\
333d522f475Smrg	if ((row) < 1) (row) = 1,(oor)=True;			\
334d522f475Smrg	else if ((row) > screen->border*2+Height(screen))	\
335d522f475Smrg	    (row) = screen->border*2+Height(screen),(oor)=True;	\
336d522f475Smrg	if ((col) < 1) (col) = 1,(oor)=True;			\
337d522f475Smrg	else if ((col) > OriginX(screen)*2+Width(screen))	\
338d522f475Smrg	    (col) = OriginX(screen)*2+Width(screen),(oor)=True;	\
339d522f475Smrg    } else {							\
340d522f475Smrg	(oor)=False;						\
341d522f475Smrg	/* Compute character position of mouse pointer */	\
342d522f475Smrg	(row) = ((y) - screen->border) / FontHeight(screen);	\
343d522f475Smrg	(col) = ((x) - OriginX(screen)) / FontWidth(screen);	\
344d522f475Smrg	/* Limit to screen dimensions */			\
345d522f475Smrg	if ((row) < 0) (row) = 0,(oor)=True;			\
346d522f475Smrg	else if ((row) > screen->max_row)			\
347d522f475Smrg	    (row) = screen->max_row,(oor)=True;			\
348d522f475Smrg	if ((col) < 0) (col) = 0,(oor)=True;			\
349d522f475Smrg	else if ((col) > screen->max_col)			\
350d522f475Smrg	    (col) = screen->max_col,(oor)=True;			\
351d522f475Smrg	(row)++; (col)++;					\
352d522f475Smrg    }
353d522f475Smrg
354d522f475Smrgstatic Bool
355894e0ac8SmrgSendLocatorPosition(XtermWidget xw, XButtonEvent *event)
356d522f475Smrg{
357d522f475Smrg    ANSI reply;
358956cc18dSsnj    TScreen *screen = TScreenOf(xw);
359d522f475Smrg    int row, col;
360d522f475Smrg    Bool oor;
361d522f475Smrg    int button;
3622eaa94a1Schristos    unsigned state;
363d522f475Smrg
364d522f475Smrg    /* Make sure the event is an appropriate type */
365492d43a5Smrg    if ((!IsBtnEvent(event) &&
366d522f475Smrg	 !screen->loc_filter) ||
367492d43a5Smrg	(BtnModifiers(event) != 0 && BtnModifiers(event) != ControlMask))
368d522f475Smrg	return (False);
369d522f475Smrg
370d522f475Smrg    if ((event->type == ButtonPress &&
371d522f475Smrg	 !(screen->locator_events & LOC_BTNS_DN)) ||
372d522f475Smrg	(event->type == ButtonRelease &&
373d522f475Smrg	 !(screen->locator_events & LOC_BTNS_UP)))
374d522f475Smrg	return (True);
375d522f475Smrg
376d522f475Smrg    if (event->type == MotionNotify) {
377d522f475Smrg	CheckLocatorPosition(xw, event);
378d522f475Smrg	return (True);
379d522f475Smrg    }
380d522f475Smrg
381d522f475Smrg    /* get button # */
382492d43a5Smrg    button = (int) event->button - 1;
383d522f475Smrg
384492d43a5Smrg    LocatorCoords(row, col, event->x, event->y, oor);
385d522f475Smrg
386d522f475Smrg    /*
387d522f475Smrg     * DECterm mouse:
388d522f475Smrg     *
389d522f475Smrg     * ESCAPE '[' event ; mask ; row ; column '&' 'w'
390d522f475Smrg     */
391d522f475Smrg    memset(&reply, 0, sizeof(reply));
392d522f475Smrg    reply.a_type = ANSI_CSI;
393d522f475Smrg
394d522f475Smrg    if (oor) {
395d522f475Smrg	reply.a_nparam = 1;
396d522f475Smrg	reply.a_param[0] = 0;	/* Event - 0 = locator unavailable */
397d522f475Smrg	reply.a_inters = '&';
398d522f475Smrg	reply.a_final = 'w';
399d522f475Smrg	unparseseq(xw, &reply);
400d522f475Smrg
401d522f475Smrg	if (screen->locator_reset) {
402d522f475Smrg	    MotionOff(screen, xw);
403d522f475Smrg	    screen->send_mouse_pos = MOUSE_OFF;
404d522f475Smrg	}
405d522f475Smrg	return (True);
406d522f475Smrg    }
407d522f475Smrg
408d522f475Smrg    /*
409d522f475Smrg     * event:
410d522f475Smrg     *        1       no buttons
411d522f475Smrg     *        2       left button down
412d522f475Smrg     *        3       left button up
413d522f475Smrg     *        4       middle button down
414d522f475Smrg     *        5       middle button up
415d522f475Smrg     *        6       right button down
416d522f475Smrg     *        7       right button up
417d522f475Smrg     *        8       M4 down
418d522f475Smrg     *        9       M4 up
419d522f475Smrg     */
420d522f475Smrg    reply.a_nparam = 4;
421d522f475Smrg    switch (event->type) {
422d522f475Smrg    case ButtonPress:
4232eaa94a1Schristos	reply.a_param[0] = (ParmType) (2 + (button << 1));
424d522f475Smrg	break;
425d522f475Smrg    case ButtonRelease:
4262eaa94a1Schristos	reply.a_param[0] = (ParmType) (3 + (button << 1));
427d522f475Smrg	break;
428d522f475Smrg    default:
429d522f475Smrg	return (True);
430d522f475Smrg    }
431d522f475Smrg    /*
432d522f475Smrg     * mask:
433913cc679Smrg     * bit7   bit6   bit5   bit4   bit3     bit2       bit1         bit0
434913cc679Smrg     *                             M4 down  left down  middle down  right down
435d522f475Smrg     *
436d522f475Smrg     * Notice that Button1 (left) and Button3 (right) are swapped in the mask.
437d522f475Smrg     * Also, mask should be the state after the button press/release,
438d522f475Smrg     * X provides the state not including the button press/release.
439d522f475Smrg     */
440492d43a5Smrg    state = (event->state
441d522f475Smrg	     & (Button1Mask | Button2Mask | Button3Mask | Button4Mask)) >> 8;
4424e40088cSchristos    /* update mask to "after" state */
44320d2c4d2Smrg    state ^= ((unsigned) (1 << button));
4444e40088cSchristos    /* swap Button1 & Button3 */
445956cc18dSsnj    state = ((state & (unsigned) ~(4 | 1))
446956cc18dSsnj	     | ((state & 1) ? 4 : 0)
447956cc18dSsnj	     | ((state & 4) ? 1 : 0));
448d522f475Smrg
4492eaa94a1Schristos    reply.a_param[1] = (ParmType) state;
4502eaa94a1Schristos    reply.a_param[2] = (ParmType) row;
4512eaa94a1Schristos    reply.a_param[3] = (ParmType) col;
452d522f475Smrg    reply.a_inters = '&';
453d522f475Smrg    reply.a_final = 'w';
454d522f475Smrg
455d522f475Smrg    unparseseq(xw, &reply);
456d522f475Smrg
457d522f475Smrg    if (screen->locator_reset) {
458d522f475Smrg	MotionOff(screen, xw);
459d522f475Smrg	screen->send_mouse_pos = MOUSE_OFF;
460d522f475Smrg    }
461d522f475Smrg
462d522f475Smrg    /*
463913cc679Smrg     * DECterm turns the Locator off if a button is pressed while a filter
464913cc679Smrg     * rectangle is active.  This might be a bug, but I don't know, so I'll
465913cc679Smrg     * emulate it anyway.
466d522f475Smrg     */
467d522f475Smrg    if (screen->loc_filter) {
468d522f475Smrg	screen->send_mouse_pos = MOUSE_OFF;
469d522f475Smrg	screen->loc_filter = False;
470d522f475Smrg	screen->locator_events = 0;
471d522f475Smrg	MotionOff(screen, xw);
472d522f475Smrg    }
473d522f475Smrg
474d522f475Smrg    return (True);
475d522f475Smrg}
476d522f475Smrg
477d522f475Smrg/*
478d522f475Smrg * mask:
479d522f475Smrg * bit 7   bit 6   bit 5   bit 4   bit 3   bit 2       bit 1         bit 0
480d522f475Smrg *                                 M4 down left down   middle down   right down
481d522f475Smrg *
482d522f475Smrg * Button1 (left) and Button3 (right) are swapped in the mask relative to X.
483d522f475Smrg */
484d522f475Smrg#define	ButtonState(state, mask)	\
485913cc679Smrg{ int stemp = (int) (((mask) & (Button1Mask | Button2Mask | Button3Mask | Button4Mask)) >> 8);	\
486d522f475Smrg  /* swap Button1 & Button3 */								\
487913cc679Smrg  (state) = (stemp & ~(4|1)) | ((stemp & 1) ? 4 : 0) | ((stemp & 4) ? 1 : 0);			\
488d522f475Smrg}
489d522f475Smrg
490d522f475Smrgvoid
491d522f475SmrgGetLocatorPosition(XtermWidget xw)
492d522f475Smrg{
493d522f475Smrg    ANSI reply;
494956cc18dSsnj    TScreen *screen = TScreenOf(xw);
495d522f475Smrg    Window root, child;
496d522f475Smrg    int rx, ry, x, y;
497d522f475Smrg    unsigned int mask;
498d522f475Smrg    int row = 0, col = 0;
499d522f475Smrg    Bool oor = False;
500d522f475Smrg    Bool ret = False;
501d522f475Smrg    int state;
502d522f475Smrg
503d522f475Smrg    /*
504913cc679Smrg     * DECterm turns the Locator off if the position is requested while a
505913cc679Smrg     * filter rectangle is active.  This might be a bug, but I don't know, so
506913cc679Smrg     * I'll emulate it anyways.
507d522f475Smrg     */
508d522f475Smrg    if (screen->loc_filter) {
509d522f475Smrg	screen->send_mouse_pos = MOUSE_OFF;
510d522f475Smrg	screen->loc_filter = False;
511d522f475Smrg	screen->locator_events = 0;
512d522f475Smrg	MotionOff(screen, xw);
513d522f475Smrg    }
514d522f475Smrg
515d522f475Smrg    memset(&reply, 0, sizeof(reply));
516d522f475Smrg    reply.a_type = ANSI_CSI;
517d522f475Smrg
518913cc679Smrg    if (okSendMousePos(xw) == DEC_LOCATOR) {
519d522f475Smrg	ret = XQueryPointer(screen->display, VWindow(screen), &root,
520d522f475Smrg			    &child, &rx, &ry, &x, &y, &mask);
521d522f475Smrg	if (ret) {
522d522f475Smrg	    LocatorCoords(row, col, x, y, oor);
523d522f475Smrg	}
524d522f475Smrg    }
525d522f475Smrg    if (ret == False || oor) {
526d522f475Smrg	reply.a_nparam = 1;
527d522f475Smrg	reply.a_param[0] = 0;	/* Event - 0 = locator unavailable */
528d522f475Smrg	reply.a_inters = '&';
529d522f475Smrg	reply.a_final = 'w';
530d522f475Smrg	unparseseq(xw, &reply);
531d522f475Smrg
532d522f475Smrg	if (screen->locator_reset) {
533d522f475Smrg	    MotionOff(screen, xw);
534d522f475Smrg	    screen->send_mouse_pos = MOUSE_OFF;
535d522f475Smrg	}
536d522f475Smrg	return;
537d522f475Smrg    }
538d522f475Smrg
539d522f475Smrg    ButtonState(state, mask);
540d522f475Smrg
541d522f475Smrg    reply.a_nparam = 4;
542d522f475Smrg    reply.a_param[0] = 1;	/* Event - 1 = response to locator request */
5432eaa94a1Schristos    reply.a_param[1] = (ParmType) state;
5442eaa94a1Schristos    reply.a_param[2] = (ParmType) row;
5452eaa94a1Schristos    reply.a_param[3] = (ParmType) col;
546d522f475Smrg    reply.a_inters = '&';
547d522f475Smrg    reply.a_final = 'w';
548d522f475Smrg    unparseseq(xw, &reply);
549d522f475Smrg
550d522f475Smrg    if (screen->locator_reset) {
551d522f475Smrg	MotionOff(screen, xw);
552d522f475Smrg	screen->send_mouse_pos = MOUSE_OFF;
553d522f475Smrg    }
554d522f475Smrg}
555d522f475Smrg
556d522f475Smrgvoid
557d522f475SmrgInitLocatorFilter(XtermWidget xw)
558d522f475Smrg{
559d522f475Smrg    ANSI reply;
560956cc18dSsnj    TScreen *screen = TScreenOf(xw);
561d522f475Smrg    Window root, child;
562d522f475Smrg    int rx, ry, x, y;
563d522f475Smrg    unsigned int mask;
564d522f475Smrg    int row = 0, col = 0;
565d522f475Smrg    Bool oor = 0;
566d522f475Smrg    Bool ret;
567d522f475Smrg
568d522f475Smrg    ret = XQueryPointer(screen->display, VWindow(screen),
569d522f475Smrg			&root, &child, &rx, &ry, &x, &y, &mask);
570d522f475Smrg    if (ret) {
571d522f475Smrg	LocatorCoords(row, col, x, y, oor);
572d522f475Smrg    }
573d522f475Smrg    if (ret == False || oor) {
574d522f475Smrg	/* Locator is unavailable */
575d522f475Smrg
576d522f475Smrg	if (screen->loc_filter_top != LOC_FILTER_POS ||
577d522f475Smrg	    screen->loc_filter_left != LOC_FILTER_POS ||
578d522f475Smrg	    screen->loc_filter_bottom != LOC_FILTER_POS ||
579d522f475Smrg	    screen->loc_filter_right != LOC_FILTER_POS) {
580d522f475Smrg	    /*
581d522f475Smrg	     * If any explicit coordinates were received,
582d522f475Smrg	     * report immediately with no coordinates.
583d522f475Smrg	     */
584d522f475Smrg	    memset(&reply, 0, sizeof(reply));
585d522f475Smrg	    reply.a_type = ANSI_CSI;
586d522f475Smrg	    reply.a_nparam = 1;
587d522f475Smrg	    reply.a_param[0] = 0;	/* Event - 0 = locator unavailable */
588d522f475Smrg	    reply.a_inters = '&';
589d522f475Smrg	    reply.a_final = 'w';
590d522f475Smrg	    unparseseq(xw, &reply);
591d522f475Smrg
592d522f475Smrg	    if (screen->locator_reset) {
593d522f475Smrg		MotionOff(screen, xw);
594d522f475Smrg		screen->send_mouse_pos = MOUSE_OFF;
595d522f475Smrg	    }
596d522f475Smrg	} else {
597d522f475Smrg	    /*
598d522f475Smrg	     * No explicit coordinates were received, and the pointer is
599d522f475Smrg	     * unavailable.  Report when the pointer re-enters the window.
600d522f475Smrg	     */
601d522f475Smrg	    screen->loc_filter = True;
602d522f475Smrg	    MotionOn(screen, xw);
603d522f475Smrg	}
604d522f475Smrg	return;
605d522f475Smrg    }
606d522f475Smrg
607d522f475Smrg    /*
608d522f475Smrg     * Adjust rectangle coordinates:
609d522f475Smrg     *  1. Replace "LOC_FILTER_POS" with current coordinates
610d522f475Smrg     *  2. Limit coordinates to screen size
611d522f475Smrg     *  3. make sure top and left are less than bottom and right, resp.
612d522f475Smrg     */
613d522f475Smrg    if (screen->locator_pixels) {
614d522f475Smrg	rx = OriginX(screen) * 2 + Width(screen);
615d522f475Smrg	ry = screen->border * 2 + Height(screen);
616d522f475Smrg    } else {
617d522f475Smrg	rx = screen->max_col;
618d522f475Smrg	ry = screen->max_row;
619d522f475Smrg    }
620d522f475Smrg
621d522f475Smrg#define	Adjust( coord, def, max )				\
622d522f475Smrg	if( (coord) == LOC_FILTER_POS )	(coord) = (def);	\
623d522f475Smrg	else if ((coord) < 1)		(coord) = 1;		\
624d522f475Smrg	else if ((coord) > (max))	(coord) = (max)
625d522f475Smrg
626d522f475Smrg    Adjust(screen->loc_filter_top, row, ry);
627d522f475Smrg    Adjust(screen->loc_filter_left, col, rx);
628d522f475Smrg    Adjust(screen->loc_filter_bottom, row, ry);
629d522f475Smrg    Adjust(screen->loc_filter_right, col, rx);
630d522f475Smrg
631d522f475Smrg    if (screen->loc_filter_top > screen->loc_filter_bottom) {
632d522f475Smrg	ry = screen->loc_filter_top;
633d522f475Smrg	screen->loc_filter_top = screen->loc_filter_bottom;
634d522f475Smrg	screen->loc_filter_bottom = ry;
635d522f475Smrg    }
636d522f475Smrg
637d522f475Smrg    if (screen->loc_filter_left > screen->loc_filter_right) {
638d522f475Smrg	rx = screen->loc_filter_left;
639d522f475Smrg	screen->loc_filter_left = screen->loc_filter_right;
640d522f475Smrg	screen->loc_filter_right = rx;
641d522f475Smrg    }
642d522f475Smrg
643d522f475Smrg    if ((col < screen->loc_filter_left) ||
644d522f475Smrg	(col > screen->loc_filter_right) ||
645d522f475Smrg	(row < screen->loc_filter_top) ||
646d522f475Smrg	(row > screen->loc_filter_bottom)) {
6472e4f8982Smrg	int state;
6482e4f8982Smrg
649d522f475Smrg	/* Pointer is already outside the rectangle - report immediately */
650d522f475Smrg	ButtonState(state, mask);
651d522f475Smrg
652d522f475Smrg	memset(&reply, 0, sizeof(reply));
653d522f475Smrg	reply.a_type = ANSI_CSI;
654d522f475Smrg	reply.a_nparam = 4;
655d522f475Smrg	reply.a_param[0] = 10;	/* Event - 10 = locator outside filter */
6562eaa94a1Schristos	reply.a_param[1] = (ParmType) state;
6572eaa94a1Schristos	reply.a_param[2] = (ParmType) row;
6582eaa94a1Schristos	reply.a_param[3] = (ParmType) col;
659d522f475Smrg	reply.a_inters = '&';
660d522f475Smrg	reply.a_final = 'w';
661d522f475Smrg	unparseseq(xw, &reply);
662d522f475Smrg
663d522f475Smrg	if (screen->locator_reset) {
664d522f475Smrg	    MotionOff(screen, xw);
665d522f475Smrg	    screen->send_mouse_pos = MOUSE_OFF;
666d522f475Smrg	}
667d522f475Smrg	return;
668d522f475Smrg    }
669d522f475Smrg
670d522f475Smrg    /*
671d522f475Smrg     * Rectangle is set up.  Allow pointer tracking
672d522f475Smrg     * to detect if the mouse leaves the rectangle.
673d522f475Smrg     */
674d522f475Smrg    screen->loc_filter = True;
675d522f475Smrg    MotionOn(screen, xw);
676d522f475Smrg}
677d522f475Smrg
678d522f475Smrgstatic void
679894e0ac8SmrgCheckLocatorPosition(XtermWidget xw, XButtonEvent *event)
680d522f475Smrg{
681d522f475Smrg    ANSI reply;
682956cc18dSsnj    TScreen *screen = TScreenOf(xw);
683d522f475Smrg    int row, col;
684d522f475Smrg    Bool oor;
685d522f475Smrg
686492d43a5Smrg    LocatorCoords(row, col, event->x, event->y, oor);
687d522f475Smrg
688d522f475Smrg    /*
689d522f475Smrg     * Send report if the pointer left the filter rectangle, if
690d522f475Smrg     * the pointer left the window, or if the filter rectangle
691d522f475Smrg     * had no coordinates and the pointer re-entered the window.
692d522f475Smrg     */
693d522f475Smrg    if (oor || (screen->loc_filter_top == LOC_FILTER_POS) ||
694d522f475Smrg	(col < screen->loc_filter_left) ||
695d522f475Smrg	(col > screen->loc_filter_right) ||
696d522f475Smrg	(row < screen->loc_filter_top) ||
697d522f475Smrg	(row > screen->loc_filter_bottom)) {
698d522f475Smrg	/* Filter triggered - disable it */
699d522f475Smrg	screen->loc_filter = False;
700d522f475Smrg	MotionOff(screen, xw);
701d522f475Smrg
702d522f475Smrg	memset(&reply, 0, sizeof(reply));
703d522f475Smrg	reply.a_type = ANSI_CSI;
704d522f475Smrg	if (oor) {
705d522f475Smrg	    reply.a_nparam = 1;
706d522f475Smrg	    reply.a_param[0] = 0;	/* Event - 0 = locator unavailable */
707d522f475Smrg	} else {
7082e4f8982Smrg	    int state;
7092e4f8982Smrg
710492d43a5Smrg	    ButtonState(state, event->state);
711d522f475Smrg
712d522f475Smrg	    reply.a_nparam = 4;
713d522f475Smrg	    reply.a_param[0] = 10;	/* Event - 10 = locator outside filter */
7142eaa94a1Schristos	    reply.a_param[1] = (ParmType) state;
7152eaa94a1Schristos	    reply.a_param[2] = (ParmType) row;
7162eaa94a1Schristos	    reply.a_param[3] = (ParmType) col;
717d522f475Smrg	}
718d522f475Smrg
719d522f475Smrg	reply.a_inters = '&';
720d522f475Smrg	reply.a_final = 'w';
721d522f475Smrg	unparseseq(xw, &reply);
722d522f475Smrg
723d522f475Smrg	if (screen->locator_reset) {
724d522f475Smrg	    MotionOff(screen, xw);
725d522f475Smrg	    screen->send_mouse_pos = MOUSE_OFF;
726d522f475Smrg	}
727d522f475Smrg    }
728d522f475Smrg}
729d522f475Smrg#endif /* OPT_DEC_LOCATOR */
730d522f475Smrg
731d522f475Smrg#if OPT_READLINE
732d522f475Smrgstatic int
733913cc679SmrgisClick1_clean(XtermWidget xw, XButtonEvent *event)
734d522f475Smrg{
735913cc679Smrg    TScreen *screen = TScreenOf(xw);
736d522f475Smrg    int delta;
737d522f475Smrg
738492d43a5Smrg    if (!IsBtnEvent(event)
739d522f475Smrg    /* Disable on Shift-Click-1, including the application-mouse modes */
740492d43a5Smrg	|| (BtnModifiers(event) & ShiftMask)
741913cc679Smrg	|| (okSendMousePos(xw) != MOUSE_OFF)	/* Kinda duplicate... */
742d522f475Smrg	||ExtendingSelection)	/* Was moved */
743d522f475Smrg	return 0;
744d522f475Smrg
745d522f475Smrg    if (event->type != ButtonRelease)
746d522f475Smrg	return 0;
747d522f475Smrg
748d522f475Smrg    if (lastButtonDownTime == (Time) 0) {
749d522f475Smrg	/* first time or once in a blue moon */
750d522f475Smrg	delta = screen->multiClickTime + 1;
751492d43a5Smrg    } else if (event->time > lastButtonDownTime) {
752d522f475Smrg	/* most of the time */
753492d43a5Smrg	delta = (int) (event->time - lastButtonDownTime);
754d522f475Smrg    } else {
755d522f475Smrg	/* time has rolled over since lastButtonUpTime */
756492d43a5Smrg	delta = (int) ((((Time) ~ 0) - lastButtonDownTime) + event->time);
757d522f475Smrg    }
758d522f475Smrg
759d522f475Smrg    return delta <= screen->multiClickTime;
760d522f475Smrg}
761d522f475Smrg
762d522f475Smrgstatic int
763894e0ac8SmrgisDoubleClick3(TScreen *screen, XButtonEvent *event)
764d522f475Smrg{
765d522f475Smrg    int delta;
766d522f475Smrg
767d522f475Smrg    if (event->type != ButtonRelease
768492d43a5Smrg	|| (BtnModifiers(event) & ShiftMask)
769492d43a5Smrg	|| event->button != Button3) {
770d522f475Smrg	lastButton3UpTime = 0;	/* Disable the cached info */
771d522f475Smrg	return 0;
772d522f475Smrg    }
773d522f475Smrg    /* Process Btn3Release. */
774d522f475Smrg    if (lastButton3DoubleDownTime == (Time) 0) {
775d522f475Smrg	/* No previous click or once in a blue moon */
776d522f475Smrg	delta = screen->multiClickTime + 1;
777492d43a5Smrg    } else if (event->time > lastButton3DoubleDownTime) {
778d522f475Smrg	/* most of the time */
779492d43a5Smrg	delta = (int) (event->time - lastButton3DoubleDownTime);
780d522f475Smrg    } else {
781d522f475Smrg	/* time has rolled over since lastButton3DoubleDownTime */
782492d43a5Smrg	delta = (int) ((((Time) ~ 0) - lastButton3DoubleDownTime) + event->time);
783d522f475Smrg    }
784d522f475Smrg    if (delta <= screen->multiClickTime) {
785d522f475Smrg	/* Double click */
786d522f475Smrg	CELL cell;
787d522f475Smrg
788d522f475Smrg	/* Cannot check ExtendingSelection, since mouse-3 always sets it */
789492d43a5Smrg	PointToCELL(screen, event->y, event->x, &cell);
790d522f475Smrg	if (isSameCELL(&cell, &lastButton3)) {
791d522f475Smrg	    lastButton3DoubleDownTime = 0;	/* Disable the third click */
792d522f475Smrg	    return 1;
793d522f475Smrg	}
794d522f475Smrg    }
795d522f475Smrg    /* Not a double click, memorize for future check. */
796492d43a5Smrg    lastButton3UpTime = event->time;
797492d43a5Smrg    PointToCELL(screen, event->y, event->x, &lastButton3);
798d522f475Smrg    return 0;
799d522f475Smrg}
800d522f475Smrg
801d522f475Smrgstatic int
802894e0ac8SmrgCheckSecondPress3(TScreen *screen, XEvent *event)
803d522f475Smrg{
804d522f475Smrg    int delta;
805d522f475Smrg
806d522f475Smrg    if (event->type != ButtonPress
807492d43a5Smrg	|| (KeyModifiers(event) & ShiftMask)
808d522f475Smrg	|| event->xbutton.button != Button3) {
809d522f475Smrg	lastButton3DoubleDownTime = 0;	/* Disable the cached info */
810d522f475Smrg	return 0;
811d522f475Smrg    }
812d522f475Smrg    /* Process Btn3Press. */
813d522f475Smrg    if (lastButton3UpTime == (Time) 0) {
814d522f475Smrg	/* No previous click or once in a blue moon */
815d522f475Smrg	delta = screen->multiClickTime + 1;
816d522f475Smrg    } else if (event->xbutton.time > lastButton3UpTime) {
817d522f475Smrg	/* most of the time */
8182eaa94a1Schristos	delta = (int) (event->xbutton.time - lastButton3UpTime);
819d522f475Smrg    } else {
820d522f475Smrg	/* time has rolled over since lastButton3UpTime */
8212eaa94a1Schristos	delta = (int) ((((Time) ~ 0) - lastButton3UpTime) + event->xbutton.time);
822d522f475Smrg    }
823d522f475Smrg    if (delta <= screen->multiClickTime) {
824d522f475Smrg	CELL cell;
825d522f475Smrg
826d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
827d522f475Smrg	if (isSameCELL(&cell, &lastButton3)) {
828d522f475Smrg	    /* A candidate for a double-click */
829d522f475Smrg	    lastButton3DoubleDownTime = event->xbutton.time;
830d522f475Smrg	    PointToCELL(screen, event->xbutton.y, event->xbutton.x, &lastButton3);
831d522f475Smrg	    return 1;
832d522f475Smrg	}
833d522f475Smrg	lastButton3UpTime = 0;	/* Disable the info about the previous click */
834d522f475Smrg    }
835d522f475Smrg    /* Either too long, or moved, disable. */
836d522f475Smrg    lastButton3DoubleDownTime = 0;
837d522f475Smrg    return 0;
838d522f475Smrg}
839d522f475Smrg
840d522f475Smrgstatic int
841e0a2b6dfSmrgrowOnCurrentLine(TScreen *screen,
842d522f475Smrg		 int line,
843d522f475Smrg		 int *deltap)	/* must be XButtonEvent */
844d522f475Smrg{
845956cc18dSsnj    int result = 1;
846d522f475Smrg
847d522f475Smrg    *deltap = 0;
8482e4f8982Smrg
849956cc18dSsnj    if (line != screen->cur_row) {
8502e4f8982Smrg	int l1, l2;
8512e4f8982Smrg
852956cc18dSsnj	if (line < screen->cur_row)
853956cc18dSsnj	    l1 = line, l2 = screen->cur_row;
854956cc18dSsnj	else
855956cc18dSsnj	    l2 = line, l1 = screen->cur_row;
856956cc18dSsnj	l1--;
857956cc18dSsnj	while (++l1 < l2) {
858956cc18dSsnj	    LineData *ld = GET_LINEDATA(screen, l1);
859956cc18dSsnj	    if (!LineTstWrapped(ld)) {
860956cc18dSsnj		result = 0;
861956cc18dSsnj		break;
862956cc18dSsnj	    }
863956cc18dSsnj	}
864956cc18dSsnj	if (result) {
865956cc18dSsnj	    /* Everything is on one "wrapped line" now */
866956cc18dSsnj	    *deltap = line - screen->cur_row;
867956cc18dSsnj	}
868956cc18dSsnj    }
869956cc18dSsnj    return result;
870d522f475Smrg}
871d522f475Smrg
872d522f475Smrgstatic int
873894e0ac8SmrgeventRow(TScreen *screen, XEvent *event)	/* must be XButtonEvent */
874d522f475Smrg{
875d522f475Smrg    return (event->xbutton.y - screen->border) / FontHeight(screen);
876d522f475Smrg}
877d522f475Smrg
878d522f475Smrgstatic int
879894e0ac8SmrgeventColBetween(TScreen *screen, XEvent *event)		/* must be XButtonEvent */
880d522f475Smrg{
881d522f475Smrg    /* Correct by half a width - we are acting on a boundary, not on a cell. */
882d522f475Smrg    return ((event->xbutton.x - OriginX(screen) + (FontWidth(screen) - 1) / 2)
883d522f475Smrg	    / FontWidth(screen));
884d522f475Smrg}
885d522f475Smrg
886d522f475Smrgstatic int
887e0a2b6dfSmrgReadLineMovePoint(TScreen *screen, int col, int ldelta)
888d522f475Smrg{
889d522f475Smrg    Char line[6];
890d522f475Smrg    unsigned count = 0;
891d522f475Smrg
892d522f475Smrg    col += ldelta * MaxCols(screen) - screen->cur_col;
893d522f475Smrg    if (col == 0)
894d522f475Smrg	return 0;
895d522f475Smrg    if (screen->control_eight_bits) {
896d522f475Smrg	line[count++] = ANSI_CSI;
897d522f475Smrg    } else {
898d522f475Smrg	line[count++] = ANSI_ESC;
899d522f475Smrg	line[count++] = '[';	/* XXX maybe sometimes O is better? */
900d522f475Smrg    }
90120d2c4d2Smrg    line[count] = CharOf(col > 0 ? 'C' : 'D');
902d522f475Smrg    if (col < 0)
903d522f475Smrg	col = -col;
904d522f475Smrg    while (col--)
905d522f475Smrg	v_write(screen->respond, line, 3);
906d522f475Smrg    return 1;
907d522f475Smrg}
908d522f475Smrg
909d522f475Smrgstatic int
910e0a2b6dfSmrgReadLineDelete(TScreen *screen, CELL *cell1, CELL *cell2)
911d522f475Smrg{
912d522f475Smrg    int del;
913d522f475Smrg
914d522f475Smrg    del = (cell2->col - cell1->col) + ((cell2->row - cell1->row) * MaxCols(screen));
915d522f475Smrg    if (del <= 0)		/* Just in case... */
916d522f475Smrg	return 0;
917d522f475Smrg    while (del--)
918492d43a5Smrg	v_write(screen->respond, (const Char *) "\177", 1);
919d522f475Smrg    return 1;
920d522f475Smrg}
921492d43a5Smrg
922492d43a5Smrgstatic void
923913cc679SmrgreadlineExtend(XtermWidget xw, XEvent *event)
924492d43a5Smrg{
925913cc679Smrg    TScreen *screen = TScreenOf(xw);
926492d43a5Smrg    int ldelta1, ldelta2;
927492d43a5Smrg
928492d43a5Smrg    if (IsBtnEvent(event)) {
929492d43a5Smrg	XButtonEvent *my_event = (XButtonEvent *) event;
930913cc679Smrg	if (isClick1_clean(xw, my_event)
931492d43a5Smrg	    && SCREEN_FLAG(screen, click1_moves)
932492d43a5Smrg	    && rowOnCurrentLine(screen, eventRow(screen, event), &ldelta1)) {
933492d43a5Smrg	    ReadLineMovePoint(screen, eventColBetween(screen, event), ldelta1);
934492d43a5Smrg	}
935492d43a5Smrg	if (isDoubleClick3(screen, my_event)
936492d43a5Smrg	    && SCREEN_FLAG(screen, dclick3_deletes)
937492d43a5Smrg	    && rowOnCurrentLine(screen, screen->startSel.row, &ldelta1)
938492d43a5Smrg	    && rowOnCurrentLine(screen, screen->endSel.row, &ldelta2)) {
939492d43a5Smrg	    ReadLineMovePoint(screen, screen->endSel.col, ldelta2);
940492d43a5Smrg	    ReadLineDelete(screen, &screen->startSel, &(screen->endSel));
941492d43a5Smrg	}
942492d43a5Smrg    }
943492d43a5Smrg}
944492d43a5Smrg
945d522f475Smrg#endif /* OPT_READLINE */
946d522f475Smrg
947d522f475Smrg/* ^XM-G<line+' '><col+' '> */
948d522f475Smrgvoid
949d522f475SmrgDiredButton(Widget w,
950894e0ac8Smrg	    XEvent *event,	/* must be XButtonEvent */
951e0a2b6dfSmrg	    String *params GCC_UNUSED,	/* selections */
952d522f475Smrg	    Cardinal *num_params GCC_UNUSED)
953d522f475Smrg{
954956cc18dSsnj    XtermWidget xw;
955956cc18dSsnj
956956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
957956cc18dSsnj	TScreen *screen = TScreenOf(xw);
958d522f475Smrg
959492d43a5Smrg	if (IsBtnEvent(event)
9602eaa94a1Schristos	    && (event->xbutton.y >= screen->border)
9612eaa94a1Schristos	    && (event->xbutton.x >= OriginX(screen))) {
9622e4f8982Smrg	    Char Line[6];
9632e4f8982Smrg	    unsigned line, col;
9642e4f8982Smrg
96520d2c4d2Smrg	    line = (unsigned) ((event->xbutton.y - screen->border)
96620d2c4d2Smrg			       / FontHeight(screen));
96720d2c4d2Smrg	    col = (unsigned) ((event->xbutton.x - OriginX(screen))
96820d2c4d2Smrg			      / FontWidth(screen));
969d522f475Smrg	    Line[0] = CONTROL('X');
970d522f475Smrg	    Line[1] = ANSI_ESC;
971d522f475Smrg	    Line[2] = 'G';
9722eaa94a1Schristos	    Line[3] = CharOf(' ' + col);
9732eaa94a1Schristos	    Line[4] = CharOf(' ' + line);
974d522f475Smrg	    v_write(screen->respond, Line, 5);
975d522f475Smrg	}
976d522f475Smrg    }
977d522f475Smrg}
978d522f475Smrg
979d522f475Smrg#if OPT_READLINE
980d522f475Smrgvoid
981d522f475SmrgReadLineButton(Widget w,
982894e0ac8Smrg	       XEvent *event,	/* must be XButtonEvent */
983e0a2b6dfSmrg	       String *params GCC_UNUSED,	/* selections */
984d522f475Smrg	       Cardinal *num_params GCC_UNUSED)
985d522f475Smrg{
986956cc18dSsnj    XtermWidget xw;
987956cc18dSsnj
988956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
989956cc18dSsnj	TScreen *screen = TScreenOf(xw);
990d522f475Smrg	Char Line[6];
991d522f475Smrg	int line, col, ldelta = 0;
992d522f475Smrg
993492d43a5Smrg	if (!IsBtnEvent(event)
994913cc679Smrg	    || (okSendMousePos(xw) != MOUSE_OFF) || ExtendingSelection)
995d522f475Smrg	    goto finish;
996d522f475Smrg	if (event->type == ButtonRelease) {
997d522f475Smrg	    int delta;
998d522f475Smrg
999d522f475Smrg	    if (lastButtonDownTime == (Time) 0) {
1000d522f475Smrg		/* first time and once in a blue moon */
1001d522f475Smrg		delta = screen->multiClickTime + 1;
1002d522f475Smrg	    } else if (event->xbutton.time > lastButtonDownTime) {
1003d522f475Smrg		/* most of the time */
10042eaa94a1Schristos		delta = (int) (event->xbutton.time - lastButtonDownTime);
1005d522f475Smrg	    } else {
1006d522f475Smrg		/* time has rolled over since lastButtonUpTime */
10072eaa94a1Schristos		delta = (int) ((((Time) ~ 0) - lastButtonDownTime) + event->xbutton.time);
1008d522f475Smrg	    }
1009d522f475Smrg	    if (delta > screen->multiClickTime)
1010d522f475Smrg		goto finish;	/* All this work for this... */
1011d522f475Smrg	}
1012d522f475Smrg	line = (event->xbutton.y - screen->border) / FontHeight(screen);
1013956cc18dSsnj	if (!rowOnCurrentLine(screen, line, &ldelta))
1014956cc18dSsnj	    goto finish;
1015d522f475Smrg	/* Correct by half a width - we are acting on a boundary, not on a cell. */
1016d522f475Smrg	col = (event->xbutton.x - OriginX(screen) + (FontWidth(screen) - 1)
1017d522f475Smrg	       / 2)
1018d522f475Smrg	    / FontWidth(screen) - screen->cur_col + ldelta * MaxCols(screen);
1019d522f475Smrg	if (col == 0)
1020d522f475Smrg	    goto finish;
1021d522f475Smrg	Line[0] = ANSI_ESC;
1022d522f475Smrg	/* XXX: sometimes it is better to send '['? */
1023d522f475Smrg	Line[1] = 'O';
10242eaa94a1Schristos	Line[2] = CharOf(col > 0 ? 'C' : 'D');
1025d522f475Smrg	if (col < 0)
1026d522f475Smrg	    col = -col;
1027d522f475Smrg	while (col--)
1028d522f475Smrg	    v_write(screen->respond, Line, 3);
1029d522f475Smrg      finish:
1030d522f475Smrg	if (event->type == ButtonRelease)
1031d522f475Smrg	    do_select_end(xw, event, params, num_params, False);
1032d522f475Smrg    }
1033d522f475Smrg}
1034d522f475Smrg#endif /* OPT_READLINE */
1035d522f475Smrg
1036d522f475Smrg/* repeats <ESC>n or <ESC>p */
1037d522f475Smrgvoid
1038d522f475SmrgViButton(Widget w,
1039894e0ac8Smrg	 XEvent *event,		/* must be XButtonEvent */
1040e0a2b6dfSmrg	 String *params GCC_UNUSED,	/* selections */
1041d522f475Smrg	 Cardinal *num_params GCC_UNUSED)
1042d522f475Smrg{
1043956cc18dSsnj    XtermWidget xw;
1044956cc18dSsnj
1045956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1046956cc18dSsnj	TScreen *screen = TScreenOf(xw);
1047d522f475Smrg	int pty = screen->respond;
1048d522f475Smrg
1049492d43a5Smrg	if (IsBtnEvent(event)) {
10502e4f8982Smrg	    int line;
1051d522f475Smrg
1052d522f475Smrg	    line = screen->cur_row -
1053d522f475Smrg		((event->xbutton.y - screen->border) / FontHeight(screen));
10542e4f8982Smrg
1055d522f475Smrg	    if (line != 0) {
10562e4f8982Smrg		Char Line[6];
10572e4f8982Smrg
1058d522f475Smrg		Line[0] = ANSI_ESC;	/* force an exit from insert-mode */
1059d522f475Smrg		v_write(pty, Line, 1);
1060d522f475Smrg
1061d522f475Smrg		if (line < 0) {
1062d522f475Smrg		    line = -line;
1063d522f475Smrg		    Line[0] = CONTROL('n');
1064d522f475Smrg		} else {
1065d522f475Smrg		    Line[0] = CONTROL('p');
1066d522f475Smrg		}
1067d522f475Smrg		while (--line >= 0)
1068d522f475Smrg		    v_write(pty, Line, 1);
1069d522f475Smrg	    }
1070d522f475Smrg	}
1071d522f475Smrg    }
1072d522f475Smrg}
1073d522f475Smrg
1074d522f475Smrg/*
1075d522f475Smrg * This function handles button-motion events
1076d522f475Smrg */
1077d522f475Smrg/*ARGSUSED*/
1078d522f475Smrgvoid
1079d522f475SmrgHandleSelectExtend(Widget w,
1080894e0ac8Smrg		   XEvent *event,	/* must be XMotionEvent */
1081e0a2b6dfSmrg		   String *params GCC_UNUSED,
1082d522f475Smrg		   Cardinal *num_params GCC_UNUSED)
1083d522f475Smrg{
1084956cc18dSsnj    XtermWidget xw;
1085956cc18dSsnj
1086956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1087956cc18dSsnj	TScreen *screen = TScreenOf(xw);
1088d522f475Smrg	CELL cell;
1089d522f475Smrg
1090e0a2b6dfSmrg	TRACE(("HandleSelectExtend @%ld\n", event->xmotion.time));
10910bd37d32Smrg
1092d522f475Smrg	screen->selection_time = event->xmotion.time;
1093d522f475Smrg	switch (screen->eventMode) {
1094d522f475Smrg	    /* If not in one of the DEC mouse-reporting modes */
1095d522f475Smrg	case LEFTEXTENSION:
1096d522f475Smrg	case RIGHTEXTENSION:
1097d522f475Smrg	    PointToCELL(screen, event->xmotion.y, event->xmotion.x, &cell);
1098d522f475Smrg	    ExtendExtend(xw, &cell);
1099d522f475Smrg	    break;
1100d522f475Smrg
1101d522f475Smrg	    /* If in motion reporting mode, send mouse position to
1102d522f475Smrg	       character process as a key sequence \E[M... */
1103d522f475Smrg	case NORMAL:
1104d522f475Smrg	    /* will get here if send_mouse_pos != MOUSE_OFF */
1105913cc679Smrg	    if (okSendMousePos(xw) == BTN_EVENT_MOUSE
1106913cc679Smrg		|| okSendMousePos(xw) == ANY_EVENT_MOUSE) {
1107d522f475Smrg		(void) SendMousePosition(xw, event);
1108d522f475Smrg	    }
1109d522f475Smrg	    break;
1110d522f475Smrg	}
1111d522f475Smrg    }
1112d522f475Smrg}
1113d522f475Smrg
1114d522f475Smrgvoid
1115d522f475SmrgHandleKeyboardSelectExtend(Widget w,
1116894e0ac8Smrg			   XEvent *event GCC_UNUSED,	/* must be XButtonEvent */
1117e0a2b6dfSmrg			   String *params GCC_UNUSED,
1118d522f475Smrg			   Cardinal *num_params GCC_UNUSED)
1119d522f475Smrg{
1120956cc18dSsnj    XtermWidget xw;
1121956cc18dSsnj
1122956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1123956cc18dSsnj	TScreen *screen = TScreenOf(xw);
11240bd37d32Smrg
11250bd37d32Smrg	TRACE(("HandleKeyboardSelectExtend\n"));
1126d522f475Smrg	ExtendExtend(xw, &screen->cursorp);
1127d522f475Smrg    }
1128d522f475Smrg}
1129d522f475Smrg
1130d522f475Smrgstatic void
1131d522f475Smrgdo_select_end(XtermWidget xw,
1132894e0ac8Smrg	      XEvent *event,	/* must be XButtonEvent */
1133e0a2b6dfSmrg	      String *params,	/* selections */
1134d522f475Smrg	      Cardinal *num_params,
1135d522f475Smrg	      Bool use_cursor_loc)
1136d522f475Smrg{
1137956cc18dSsnj    TScreen *screen = TScreenOf(xw);
1138d522f475Smrg
1139d522f475Smrg    screen->selection_time = event->xbutton.time;
1140e0a2b6dfSmrg    TRACE(("do_select_end @%ld\n", screen->selection_time));
1141d522f475Smrg    switch (screen->eventMode) {
1142d522f475Smrg    case NORMAL:
1143d522f475Smrg	(void) SendMousePosition(xw, event);
1144d522f475Smrg	break;
1145d522f475Smrg    case LEFTEXTENSION:
1146d522f475Smrg    case RIGHTEXTENSION:
1147d522f475Smrg	EndExtend(xw, event, params, *num_params, use_cursor_loc);
1148d522f475Smrg#if OPT_READLINE
1149913cc679Smrg	readlineExtend(xw, event);
1150d522f475Smrg#endif /* OPT_READLINE */
1151d522f475Smrg	break;
1152d522f475Smrg    }
1153d522f475Smrg}
1154d522f475Smrg
1155d522f475Smrgvoid
1156d522f475SmrgHandleSelectEnd(Widget w,
1157894e0ac8Smrg		XEvent *event,	/* must be XButtonEvent */
1158e0a2b6dfSmrg		String *params,	/* selections */
1159d522f475Smrg		Cardinal *num_params)
1160d522f475Smrg{
1161956cc18dSsnj    XtermWidget xw;
1162956cc18dSsnj
1163956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
11640bd37d32Smrg	TRACE(("HandleSelectEnd\n"));
1165956cc18dSsnj	do_select_end(xw, event, params, num_params, False);
1166956cc18dSsnj    }
1167d522f475Smrg}
1168d522f475Smrg
1169d522f475Smrgvoid
1170d522f475SmrgHandleKeyboardSelectEnd(Widget w,
1171894e0ac8Smrg			XEvent *event,	/* must be XButtonEvent */
1172e0a2b6dfSmrg			String *params,		/* selections */
1173d522f475Smrg			Cardinal *num_params)
1174d522f475Smrg{
1175956cc18dSsnj    XtermWidget xw;
1176956cc18dSsnj
1177956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
11780bd37d32Smrg	TRACE(("HandleKeyboardSelectEnd\n"));
1179956cc18dSsnj	do_select_end(xw, event, params, num_params, True);
1180956cc18dSsnj    }
1181d522f475Smrg}
1182d522f475Smrg
1183492d43a5Smrg/*
11846879286fSmrg * Copy the selection data to the given target(s).
1185492d43a5Smrg */
1186492d43a5Smrgvoid
11876879286fSmrgHandleCopySelection(Widget w,
1188894e0ac8Smrg		    XEvent *event,
1189e0a2b6dfSmrg		    String *params,	/* list of targets */
11906879286fSmrg		    Cardinal *num_params)
1191492d43a5Smrg{
1192492d43a5Smrg    XtermWidget xw;
1193492d43a5Smrg
1194492d43a5Smrg    if ((xw = getXtermWidget(w)) != 0) {
11950bd37d32Smrg	TRACE(("HandleCopySelection\n"));
11966879286fSmrg	SelectSet(xw, event, params, *num_params);
1197492d43a5Smrg    }
1198492d43a5Smrg}
1199492d43a5Smrg
1200d522f475Smrgstruct _SelectionList {
1201d522f475Smrg    String *params;
1202d522f475Smrg    Cardinal count;
1203d522f475Smrg    Atom *targets;
1204d522f475Smrg    Time time;
1205d522f475Smrg};
1206d522f475Smrg
1207d522f475Smrgstatic unsigned
1208d522f475SmrgDECtoASCII(unsigned ch)
1209d522f475Smrg{
1210d522f475Smrg    if (xtermIsDecGraphic(ch)) {
12112eaa94a1Schristos	ch = CharOf("###########+++++##-##++++|######"[ch]);
12122eaa94a1Schristos	/*           01234567890123456789012345678901 */
1213d522f475Smrg    }
1214d522f475Smrg    return ch;
1215d522f475Smrg}
121620d2c4d2Smrg
121720d2c4d2Smrg#if OPT_WIDE_CHARS
121820d2c4d2Smrgstatic Cardinal
1219e0a2b6dfSmrgaddXtermChar(Char **buffer, Cardinal *used, Cardinal offset, unsigned value)
122020d2c4d2Smrg{
122120d2c4d2Smrg    if (offset + 1 >= *used) {
122220d2c4d2Smrg	*used = 1 + (2 * (offset + 1));
122320d2c4d2Smrg	allocXtermChars(buffer, *used);
122420d2c4d2Smrg    }
122520d2c4d2Smrg    (*buffer)[offset++] = (Char) value;
122620d2c4d2Smrg    return offset;
122720d2c4d2Smrg}
122820d2c4d2Smrg#define AddChar(buffer, used, offset, value) \
122920d2c4d2Smrg	offset = addXtermChar(buffer, used, offset, (unsigned) value)
123020d2c4d2Smrg
1231d522f475Smrg/*
1232d522f475Smrg * Convert a UTF-8 string to Latin-1, replacing non Latin-1 characters by `#',
1233d522f475Smrg * or ASCII/Latin-1 equivalents for special cases.
1234d522f475Smrg */
1235d522f475Smrgstatic Char *
1236e0a2b6dfSmrgUTF8toLatin1(TScreen *screen, Char *s, unsigned long len, unsigned long *result)
1237d522f475Smrg{
1238d522f475Smrg    static Char *buffer;
1239956cc18dSsnj    static Cardinal used;
1240d522f475Smrg
124120d2c4d2Smrg    Cardinal offset = 0;
1242d522f475Smrg
124320d2c4d2Smrg    if (len != 0) {
1244d522f475Smrg	PtyData data;
1245d522f475Smrg
1246d522f475Smrg	fakePtyData(&data, s, s + len);
1247894e0ac8Smrg	while (decodeUtf8(screen, &data)) {
1248956cc18dSsnj	    Bool fails = False;
1249956cc18dSsnj	    Bool extra = False;
1250d522f475Smrg	    IChar value = skipPtyData(&data);
1251d522f475Smrg	    if (value == UCS_REPL) {
1252956cc18dSsnj		fails = True;
1253d522f475Smrg	    } else if (value < 256) {
125420d2c4d2Smrg		AddChar(&buffer, &used, offset, CharOf(value));
1255d522f475Smrg	    } else {
1256d522f475Smrg		unsigned eqv = ucs2dec(value);
1257d522f475Smrg		if (xtermIsDecGraphic(eqv)) {
125820d2c4d2Smrg		    AddChar(&buffer, &used, offset, DECtoASCII(eqv));
1259d522f475Smrg		} else {
1260d522f475Smrg		    eqv = AsciiEquivs(value);
1261956cc18dSsnj		    if (eqv == value) {
1262956cc18dSsnj			fails = True;
1263956cc18dSsnj		    } else {
126420d2c4d2Smrg			AddChar(&buffer, &used, offset, eqv);
1265956cc18dSsnj		    }
1266956cc18dSsnj		    if (isWide((wchar_t) value))
1267956cc18dSsnj			extra = True;
1268956cc18dSsnj		}
1269956cc18dSsnj	    }
1270956cc18dSsnj
1271956cc18dSsnj	    /*
1272956cc18dSsnj	     * If we're not able to plug in a single-byte result, insert the
1273956cc18dSsnj	     * defaultString (which normally is a single "#", but could be
1274956cc18dSsnj	     * whatever the user wants).
1275956cc18dSsnj	     */
1276956cc18dSsnj	    if (fails) {
12772e4f8982Smrg		const Char *p;
12782e4f8982Smrg
1279492d43a5Smrg		for (p = (const Char *) screen->default_string; *p != '\0'; ++p) {
128020d2c4d2Smrg		    AddChar(&buffer, &used, offset, *p);
1281d522f475Smrg		}
1282d522f475Smrg	    }
1283956cc18dSsnj	    if (extra)
128420d2c4d2Smrg		AddChar(&buffer, &used, offset, ' ');
1285d522f475Smrg	}
128620d2c4d2Smrg	AddChar(&buffer, &used, offset, '\0');
128720d2c4d2Smrg	*result = (unsigned long) (offset - 1);
1288d522f475Smrg    } else {
1289d522f475Smrg	*result = 0;
1290d522f475Smrg    }
1291d522f475Smrg    return buffer;
1292d522f475Smrg}
129320d2c4d2Smrg
129420d2c4d2Smrgint
129520d2c4d2SmrgxtermUtf8ToTextList(XtermWidget xw,
129620d2c4d2Smrg		    XTextProperty * text_prop,
129720d2c4d2Smrg		    char ***text_list,
129820d2c4d2Smrg		    int *text_list_count)
129920d2c4d2Smrg{
130020d2c4d2Smrg    TScreen *screen = TScreenOf(xw);
130120d2c4d2Smrg    Display *dpy = screen->display;
130220d2c4d2Smrg    int rc = -1;
130320d2c4d2Smrg
130420d2c4d2Smrg    if (text_prop->format == 8
130520d2c4d2Smrg	&& (rc = Xutf8TextPropertyToTextList(dpy, text_prop,
130620d2c4d2Smrg					     text_list,
130720d2c4d2Smrg					     text_list_count)) >= 0) {
130820d2c4d2Smrg	if (*text_list != NULL && *text_list_count != 0) {
130920d2c4d2Smrg	    int i;
131020d2c4d2Smrg	    Char *data;
131120d2c4d2Smrg	    char **new_text_list, *tmp;
131220d2c4d2Smrg	    unsigned long size, new_size;
131320d2c4d2Smrg
131420d2c4d2Smrg	    TRACE(("xtermUtf8ToTextList size %d\n", *text_list_count));
131520d2c4d2Smrg
131620d2c4d2Smrg	    /*
131720d2c4d2Smrg	     * XLib StringList actually uses only two pointers, one for the
131820d2c4d2Smrg	     * list itself, and one for the data.  Pointer to the data is the
131920d2c4d2Smrg	     * first element of the list, the rest (if any) list elements point
132020d2c4d2Smrg	     * to the same memory block as the first element
132120d2c4d2Smrg	     */
132220d2c4d2Smrg	    new_size = 0;
132320d2c4d2Smrg	    for (i = 0; i < *text_list_count; ++i) {
132420d2c4d2Smrg		data = (Char *) (*text_list)[i];
132520d2c4d2Smrg		size = strlen((*text_list)[i]) + 1;
132620d2c4d2Smrg		(void) UTF8toLatin1(screen, data, size, &size);
132720d2c4d2Smrg		new_size += size + 1;
132820d2c4d2Smrg	    }
1329a1f3da82Smrg	    new_text_list = TypeXtMallocN(char *, *text_list_count);
133020d2c4d2Smrg	    new_text_list[0] = tmp = XtMalloc((Cardinal) new_size);
133120d2c4d2Smrg	    for (i = 0; i < (*text_list_count); ++i) {
133220d2c4d2Smrg		data = (Char *) (*text_list)[i];
133320d2c4d2Smrg		size = strlen((*text_list)[i]) + 1;
13340bd37d32Smrg		if ((data = UTF8toLatin1(screen, data, size, &size)) != 0) {
13350bd37d32Smrg		    memcpy(tmp, data, size + 1);
13360bd37d32Smrg		    new_text_list[i] = tmp;
13370bd37d32Smrg		    tmp += size + 1;
13380bd37d32Smrg		}
133920d2c4d2Smrg	    }
134020d2c4d2Smrg	    XFreeStringList((*text_list));
134120d2c4d2Smrg	    *text_list = new_text_list;
134220d2c4d2Smrg	} else {
134320d2c4d2Smrg	    rc = -1;
134420d2c4d2Smrg	}
134520d2c4d2Smrg    }
134620d2c4d2Smrg    return rc;
134720d2c4d2Smrg}
1348d522f475Smrg#endif /* OPT_WIDE_CHARS */
1349d522f475Smrg
1350956cc18dSsnjstatic char *
1351956cc18dSsnjparseItem(char *value, char *nextc)
1352d522f475Smrg{
1353956cc18dSsnj    char *nextp = value;
1354956cc18dSsnj    while (*nextp != '\0' && *nextp != ',') {
1355956cc18dSsnj	*nextp = x_toupper(*nextp);
1356956cc18dSsnj	++nextp;
1357956cc18dSsnj    }
1358956cc18dSsnj    *nextc = *nextp;
1359956cc18dSsnj    *nextp = '\0';
1360d522f475Smrg
1361956cc18dSsnj    return nextp;
1362956cc18dSsnj}
1363d522f475Smrg
1364956cc18dSsnj/*
1365956cc18dSsnj * All of the wanted strings are unique in the first character, so we can
1366956cc18dSsnj * use simple abbreviations.
1367956cc18dSsnj */
1368956cc18dSsnjstatic Bool
1369956cc18dSsnjsameItem(const char *actual, const char *wanted)
1370956cc18dSsnj{
1371956cc18dSsnj    Bool result = False;
1372956cc18dSsnj    size_t have = strlen(actual);
1373956cc18dSsnj    size_t need = strlen(wanted);
1374d522f475Smrg
1375956cc18dSsnj    if (have != 0 && have <= need) {
1376956cc18dSsnj	if (!strncmp(actual, wanted, have)) {
1377956cc18dSsnj	    TRACE(("...matched \"%s\"\n", wanted));
1378956cc18dSsnj	    result = True;
1379956cc18dSsnj	}
1380956cc18dSsnj    }
1381956cc18dSsnj
1382956cc18dSsnj    return result;
1383956cc18dSsnj}
1384956cc18dSsnj
1385956cc18dSsnj/*
1386956cc18dSsnj * Handle the eightBitSelectTypes or utf8SelectTypes resource values.
1387956cc18dSsnj */
1388956cc18dSsnjstatic Bool
1389894e0ac8SmrgoverrideTargets(Widget w, String value, Atom **resultp)
1390956cc18dSsnj{
1391956cc18dSsnj    Bool override = False;
1392956cc18dSsnj    XtermWidget xw;
1393956cc18dSsnj
1394956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
1395956cc18dSsnj	TScreen *screen = TScreenOf(xw);
1396956cc18dSsnj
139720d2c4d2Smrg	if (!IsEmpty(value)) {
1398492d43a5Smrg	    char *copied = x_strdup(value);
1399956cc18dSsnj	    if (copied != 0) {
1400956cc18dSsnj		Atom *result = 0;
1401956cc18dSsnj		Cardinal count = 1;
1402956cc18dSsnj		int n;
1403d522f475Smrg
1404956cc18dSsnj		TRACE(("decoding SelectTypes \"%s\"\n", value));
1405956cc18dSsnj		for (n = 0; copied[n] != '\0'; ++n) {
1406956cc18dSsnj		    if (copied[n] == ',')
1407956cc18dSsnj			++count;
1408956cc18dSsnj		}
1409a1f3da82Smrg		result = TypeXtMallocN(Atom, (2 * count) + 1);
1410956cc18dSsnj		if (result == NULL) {
1411956cc18dSsnj		    TRACE(("Couldn't allocate selection types\n"));
1412956cc18dSsnj		} else {
1413956cc18dSsnj		    char nextc = '?';
141420d2c4d2Smrg		    char *listp = (char *) copied;
1415956cc18dSsnj		    count = 0;
1416956cc18dSsnj		    do {
1417956cc18dSsnj			char *nextp = parseItem(listp, &nextc);
14180bd37d32Smrg			char *item = x_strtrim(listp);
14190bd37d32Smrg			size_t len = (item ? strlen(item) : 0);
1420956cc18dSsnj
1421956cc18dSsnj			if (len == 0) {
1422a1f3da82Smrg			    /* EMPTY */ ;
1423956cc18dSsnj			}
1424956cc18dSsnj#if OPT_WIDE_CHARS
14250bd37d32Smrg			else if (sameItem(item, "UTF8")) {
1426956cc18dSsnj			    result[count++] = XA_UTF8_STRING(XtDisplay(w));
1427956cc18dSsnj			}
1428956cc18dSsnj#endif
14290bd37d32Smrg			else if (sameItem(item, "I18N")) {
1430956cc18dSsnj			    if (screen->i18nSelections) {
1431956cc18dSsnj				result[count++] = XA_TEXT(XtDisplay(w));
1432956cc18dSsnj				result[count++] = XA_COMPOUND_TEXT(XtDisplay(w));
1433956cc18dSsnj			    }
14340bd37d32Smrg			} else if (sameItem(item, "TEXT")) {
1435956cc18dSsnj			    result[count++] = XA_TEXT(XtDisplay(w));
14360bd37d32Smrg			} else if (sameItem(item, "COMPOUND_TEXT")) {
1437956cc18dSsnj			    result[count++] = XA_COMPOUND_TEXT(XtDisplay(w));
14380bd37d32Smrg			} else if (sameItem(item, "STRING")) {
1439956cc18dSsnj			    result[count++] = XA_STRING;
1440956cc18dSsnj			}
1441956cc18dSsnj			*nextp++ = nextc;
1442956cc18dSsnj			listp = nextp;
14430bd37d32Smrg			free(item);
1444956cc18dSsnj		    } while (nextc != '\0');
1445956cc18dSsnj		    if (count) {
1446956cc18dSsnj			result[count] = None;
1447956cc18dSsnj			override = True;
1448956cc18dSsnj			*resultp = result;
1449956cc18dSsnj		    } else {
1450956cc18dSsnj			XtFree((char *) result);
1451956cc18dSsnj		    }
1452956cc18dSsnj		}
14530bd37d32Smrg		free(copied);
1454956cc18dSsnj	    } else {
1455956cc18dSsnj		TRACE(("Couldn't allocate copy of selection types\n"));
1456d522f475Smrg	    }
1457956cc18dSsnj	}
1458956cc18dSsnj    }
1459956cc18dSsnj    return override;
1460956cc18dSsnj}
1461956cc18dSsnj
1462956cc18dSsnj#if OPT_WIDE_CHARS
1463956cc18dSsnjstatic Atom *
1464e0a2b6dfSmrgallocUtf8Targets(Widget w, TScreen *screen)
1465956cc18dSsnj{
1466956cc18dSsnj    Atom **resultp = &(screen->selection_targets_utf8);
1467956cc18dSsnj
1468956cc18dSsnj    if (*resultp == 0) {
1469956cc18dSsnj	Atom *result;
1470956cc18dSsnj
1471956cc18dSsnj	if (!overrideTargets(w, screen->utf8_select_types, &result)) {
1472a1f3da82Smrg	    result = TypeXtMallocN(Atom, 5);
1473956cc18dSsnj	    if (result == NULL) {
1474956cc18dSsnj		TRACE(("Couldn't allocate utf-8 selection targets\n"));
1475956cc18dSsnj	    } else {
1476956cc18dSsnj		int n = 0;
1477956cc18dSsnj
1478e39b573cSmrg		if (XSupportsLocale()) {
1479e39b573cSmrg		    result[n++] = XA_UTF8_STRING(XtDisplay(w));
1480d522f475Smrg#ifdef X_HAVE_UTF8_STRING
1481e39b573cSmrg		    if (screen->i18nSelections) {
1482e39b573cSmrg			result[n++] = XA_TEXT(XtDisplay(w));
1483e39b573cSmrg			result[n++] = XA_COMPOUND_TEXT(XtDisplay(w));
1484e39b573cSmrg		    }
1485d522f475Smrg#endif
1486e39b573cSmrg		}
1487956cc18dSsnj		result[n++] = XA_STRING;
1488956cc18dSsnj		result[n] = None;
1489956cc18dSsnj	    }
1490d522f475Smrg	}
1491956cc18dSsnj
1492956cc18dSsnj	*resultp = result;
1493d522f475Smrg    }
1494956cc18dSsnj
1495956cc18dSsnj    return *resultp;
1496956cc18dSsnj}
1497d522f475Smrg#endif
1498d522f475Smrg
1499956cc18dSsnjstatic Atom *
1500e0a2b6dfSmrgalloc8bitTargets(Widget w, TScreen *screen)
1501956cc18dSsnj{
1502956cc18dSsnj    Atom **resultp = &(screen->selection_targets_8bit);
1503956cc18dSsnj
1504956cc18dSsnj    if (*resultp == 0) {
1505956cc18dSsnj	Atom *result = 0;
1506956cc18dSsnj
1507956cc18dSsnj	if (!overrideTargets(w, screen->eightbit_select_types, &result)) {
1508a1f3da82Smrg	    result = TypeXtMallocN(Atom, 5);
1509956cc18dSsnj	    if (result == NULL) {
1510956cc18dSsnj		TRACE(("Couldn't allocate 8bit selection targets\n"));
1511956cc18dSsnj	    } else {
1512956cc18dSsnj		int n = 0;
1513956cc18dSsnj
1514e39b573cSmrg		if (XSupportsLocale()) {
1515d522f475Smrg#ifdef X_HAVE_UTF8_STRING
1516e39b573cSmrg		    result[n++] = XA_UTF8_STRING(XtDisplay(w));
1517956cc18dSsnj#endif
1518e39b573cSmrg		    if (screen->i18nSelections) {
1519e39b573cSmrg			result[n++] = XA_TEXT(XtDisplay(w));
1520e39b573cSmrg			result[n++] = XA_COMPOUND_TEXT(XtDisplay(w));
1521e39b573cSmrg		    }
1522956cc18dSsnj		}
1523956cc18dSsnj		result[n++] = XA_STRING;
1524956cc18dSsnj		result[n] = None;
1525956cc18dSsnj	    }
1526956cc18dSsnj	}
1527956cc18dSsnj
1528956cc18dSsnj	*resultp = result;
1529956cc18dSsnj    }
1530956cc18dSsnj
1531956cc18dSsnj    return *resultp;
1532956cc18dSsnj}
1533956cc18dSsnj
1534956cc18dSsnjstatic Atom *
1535956cc18dSsnj_SelectionTargets(Widget w)
1536956cc18dSsnj{
1537956cc18dSsnj    Atom *result;
1538956cc18dSsnj    XtermWidget xw;
1539956cc18dSsnj
1540956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0) {
1541956cc18dSsnj	result = NULL;
1542956cc18dSsnj    } else {
15432e4f8982Smrg	TScreen *screen = TScreenOf(xw);
1544956cc18dSsnj
1545956cc18dSsnj#if OPT_WIDE_CHARS
1546956cc18dSsnj	if (screen->wide_chars) {
1547956cc18dSsnj	    result = allocUtf8Targets(w, screen);
1548956cc18dSsnj	} else
1549d522f475Smrg#endif
1550956cc18dSsnj	{
1551956cc18dSsnj	    /* not screen->wide_chars */
1552956cc18dSsnj	    result = alloc8bitTargets(w, screen);
1553d522f475Smrg	}
1554d522f475Smrg    }
1555956cc18dSsnj
1556956cc18dSsnj    return result;
1557d522f475Smrg}
1558d522f475Smrg
1559d522f475Smrg#define isSELECT(value) (!strcmp(value, "SELECT"))
1560d522f475Smrg
1561d522f475Smrgstatic void
1562d522f475SmrgUnmapSelections(XtermWidget xw)
1563d522f475Smrg{
1564956cc18dSsnj    TScreen *screen = TScreenOf(xw);
1565d522f475Smrg    Cardinal n;
1566d522f475Smrg
1567d522f475Smrg    if (screen->mappedSelect) {
1568d522f475Smrg	for (n = 0; screen->mappedSelect[n] != 0; ++n)
156920d2c4d2Smrg	    free((void *) screen->mappedSelect[n]);
1570d522f475Smrg	free(screen->mappedSelect);
1571d522f475Smrg	screen->mappedSelect = 0;
1572d522f475Smrg    }
1573d522f475Smrg}
1574d522f475Smrg
1575d522f475Smrg/*
1576d522f475Smrg * xterm generally uses the primary selection.  Some applications prefer
1577d522f475Smrg * (or are limited to) the clipboard.  Since the translations resource is
1578d522f475Smrg * complicated, users seldom change the way it affects selection.  But it
1579d522f475Smrg * is simple to remap the choice between primary and clipboard before the
1580d522f475Smrg * call to XmuInternStrings().
1581d522f475Smrg */
1582d522f475Smrgstatic String *
1583e0a2b6dfSmrgMapSelections(XtermWidget xw, String *params, Cardinal num_params)
1584d522f475Smrg{
1585d522f475Smrg    String *result = params;
1586d522f475Smrg
1587913cc679Smrg    if (params != 0 && num_params > 0) {
1588d522f475Smrg	Cardinal j;
1589d522f475Smrg	Boolean map = False;
1590d522f475Smrg
1591d522f475Smrg	for (j = 0; j < num_params; ++j) {
1592d522f475Smrg	    TRACE(("param[%d]:%s\n", j, params[j]));
1593d522f475Smrg	    if (isSELECT(params[j])) {
1594d522f475Smrg		map = True;
1595d522f475Smrg		break;
1596d522f475Smrg	    }
1597d522f475Smrg	}
1598d522f475Smrg	if (map) {
1599956cc18dSsnj	    TScreen *screen = TScreenOf(xw);
1600956cc18dSsnj	    const char *mapTo = (screen->selectToClipboard
1601d522f475Smrg				 ? "CLIPBOARD"
1602d522f475Smrg				 : "PRIMARY");
1603d522f475Smrg
1604d522f475Smrg	    UnmapSelections(xw);
1605d522f475Smrg	    if ((result = TypeMallocN(String, num_params + 1)) != 0) {
1606d522f475Smrg		result[num_params] = 0;
1607d522f475Smrg		for (j = 0; j < num_params; ++j) {
1608d522f475Smrg		    result[j] = x_strdup((isSELECT(params[j])
1609d522f475Smrg					  ? mapTo
1610d522f475Smrg					  : params[j]));
1611d522f475Smrg		    if (result[j] == 0) {
1612d522f475Smrg			UnmapSelections(xw);
16130bd37d32Smrg			while (j != 0) {
16140bd37d32Smrg			    free((void *) result[--j]);
16150bd37d32Smrg			}
16160bd37d32Smrg			free(result);
1617d522f475Smrg			result = 0;
1618d522f475Smrg			break;
1619d522f475Smrg		    }
1620d522f475Smrg		}
1621956cc18dSsnj		screen->mappedSelect = result;
1622d522f475Smrg	    }
1623d522f475Smrg	}
1624d522f475Smrg    }
1625d522f475Smrg    return result;
1626d522f475Smrg}
1627d522f475Smrg
1628d522f475Smrg/*
1629d522f475Smrg * Lookup the cut-buffer number, which will be in the range 0-7.
1630d522f475Smrg * If it is not a cut-buffer, it is the primary selection (-1).
1631d522f475Smrg */
1632d522f475Smrgstatic int
163320d2c4d2SmrgCutBuffer(Atom code)
1634d522f475Smrg{
1635d522f475Smrg    int cutbuffer;
163620d2c4d2Smrg    switch ((unsigned) code) {
1637d522f475Smrg    case XA_CUT_BUFFER0:
1638d522f475Smrg	cutbuffer = 0;
1639d522f475Smrg	break;
1640d522f475Smrg    case XA_CUT_BUFFER1:
1641d522f475Smrg	cutbuffer = 1;
1642d522f475Smrg	break;
1643d522f475Smrg    case XA_CUT_BUFFER2:
1644d522f475Smrg	cutbuffer = 2;
1645d522f475Smrg	break;
1646d522f475Smrg    case XA_CUT_BUFFER3:
1647d522f475Smrg	cutbuffer = 3;
1648d522f475Smrg	break;
1649d522f475Smrg    case XA_CUT_BUFFER4:
1650d522f475Smrg	cutbuffer = 4;
1651d522f475Smrg	break;
1652d522f475Smrg    case XA_CUT_BUFFER5:
1653d522f475Smrg	cutbuffer = 5;
1654d522f475Smrg	break;
1655d522f475Smrg    case XA_CUT_BUFFER6:
1656d522f475Smrg	cutbuffer = 6;
1657d522f475Smrg	break;
1658d522f475Smrg    case XA_CUT_BUFFER7:
1659d522f475Smrg	cutbuffer = 7;
1660d522f475Smrg	break;
1661d522f475Smrg    default:
1662d522f475Smrg	cutbuffer = -1;
1663d522f475Smrg	break;
1664d522f475Smrg    }
16650bd37d32Smrg    TRACE(("CutBuffer(%d) = %d\n", (int) code, cutbuffer));
1666d522f475Smrg    return cutbuffer;
1667d522f475Smrg}
1668d522f475Smrg
1669d522f475Smrg#if OPT_PASTE64
1670d522f475Smrgstatic void
1671d522f475SmrgFinishPaste64(XtermWidget xw)
1672d522f475Smrg{
1673956cc18dSsnj    TScreen *screen = TScreenOf(xw);
1674956cc18dSsnj
1675956cc18dSsnj    TRACE(("FinishPaste64(%d)\n", screen->base64_paste));
1676956cc18dSsnj    if (screen->base64_paste) {
1677956cc18dSsnj	screen->base64_paste = 0;
1678956cc18dSsnj	unparseputc1(xw, screen->base64_final);
1679d522f475Smrg	unparse_end(xw);
1680d522f475Smrg    }
1681d522f475Smrg}
1682d522f475Smrg#endif
1683d522f475Smrg
1684d522f475Smrg#if !OPT_PASTE64
1685d522f475Smrgstatic
1686d522f475Smrg#endif
1687d522f475Smrgvoid
1688d522f475SmrgxtermGetSelection(Widget w,
1689d522f475Smrg		  Time ev_time,
1690e0a2b6dfSmrg		  String *params,	/* selections in precedence order */
1691d522f475Smrg		  Cardinal num_params,
1692894e0ac8Smrg		  Atom *targets)
1693d522f475Smrg{
1694d522f475Smrg    Atom selection;
1695d522f475Smrg    int cutbuffer;
1696d522f475Smrg    Atom target;
1697d522f475Smrg
1698956cc18dSsnj    XtermWidget xw;
1699956cc18dSsnj
170020d2c4d2Smrg    if (num_params == 0)
170120d2c4d2Smrg	return;
1702956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0)
1703d522f475Smrg	return;
1704d522f475Smrg
1705e0a2b6dfSmrg    TRACE(("xtermGetSelection num_params %d @%ld\n", num_params, ev_time));
1706956cc18dSsnj    params = MapSelections(xw, params, num_params);
1707d522f475Smrg
1708d522f475Smrg    XmuInternStrings(XtDisplay(w), params, (Cardinal) 1, &selection);
1709d522f475Smrg    cutbuffer = CutBuffer(selection);
1710d522f475Smrg
1711956cc18dSsnj    TRACE(("Cutbuffer: %d, target: %s\n", cutbuffer,
1712956cc18dSsnj	   (targets
1713956cc18dSsnj	    ? visibleSelectionTarget(XtDisplay(w), targets[0])
1714956cc18dSsnj	    : "None")));
1715d522f475Smrg
1716d522f475Smrg    if (cutbuffer >= 0) {
1717d522f475Smrg	int inbytes;
1718d522f475Smrg	unsigned long nbytes;
1719d522f475Smrg	int fmt8 = 8;
1720d522f475Smrg	Atom type = XA_STRING;
1721d522f475Smrg	char *line;
1722d522f475Smrg
1723d522f475Smrg	/* 'line' is freed in SelectionReceived */
1724d522f475Smrg	line = XFetchBuffer(XtDisplay(w), &inbytes, cutbuffer);
1725d522f475Smrg	nbytes = (unsigned long) inbytes;
1726d522f475Smrg
17270bd37d32Smrg	if (nbytes > 0) {
1728d522f475Smrg	    SelectionReceived(w, NULL, &selection, &type, (XtPointer) line,
1729d522f475Smrg			      &nbytes, &fmt8);
17300bd37d32Smrg	} else if (num_params > 1) {
1731d522f475Smrg	    xtermGetSelection(w, ev_time, params + 1, num_params - 1, NULL);
1732d522f475Smrg	}
1733d522f475Smrg#if OPT_PASTE64
1734d522f475Smrg	else {
1735956cc18dSsnj	    FinishPaste64(xw);
1736d522f475Smrg	}
1737d522f475Smrg#endif
1738d522f475Smrg    } else {
1739d522f475Smrg
1740d522f475Smrg	if (targets == NULL || targets[0] == None) {
1741d522f475Smrg	    targets = _SelectionTargets(w);
1742d522f475Smrg	}
1743d522f475Smrg
1744d522f475Smrg	if (targets != 0) {
17452e4f8982Smrg	    struct _SelectionList *list;
17462e4f8982Smrg
1747d522f475Smrg	    target = targets[0];
1748d522f475Smrg
1749d522f475Smrg	    if (targets[1] == None) {	/* last target in list */
1750d522f475Smrg		params++;
1751d522f475Smrg		num_params--;
1752d522f475Smrg		targets = _SelectionTargets(w);
1753d522f475Smrg	    } else {
1754d522f475Smrg		targets = &(targets[1]);
1755d522f475Smrg	    }
1756d522f475Smrg
1757d522f475Smrg	    if (num_params) {
1758d522f475Smrg		/* 'list' is freed in SelectionReceived */
1759a1f3da82Smrg		list = TypeXtMalloc(struct _SelectionList);
1760d522f475Smrg		if (list != 0) {
1761d522f475Smrg		    list->params = params;
1762d522f475Smrg		    list->count = num_params;
1763d522f475Smrg		    list->targets = targets;
1764d522f475Smrg		    list->time = ev_time;
1765d522f475Smrg		}
1766d522f475Smrg	    } else {
1767d522f475Smrg		list = NULL;
1768d522f475Smrg	    }
1769d522f475Smrg
1770d522f475Smrg	    XtGetSelectionValue(w, selection,
1771d522f475Smrg				target,
1772d522f475Smrg				SelectionReceived,
1773d522f475Smrg				(XtPointer) list, ev_time);
1774d522f475Smrg	}
1775d522f475Smrg    }
1776d522f475Smrg}
1777d522f475Smrg
1778d522f475Smrg#if OPT_TRACE && OPT_WIDE_CHARS
1779d522f475Smrgstatic void
1780e0a2b6dfSmrgGettingSelection(Display *dpy, Atom type, Char *line, unsigned long len)
1781d522f475Smrg{
1782d522f475Smrg    Char *cp;
1783913cc679Smrg    const char *name = TraceAtomName(dpy, type);
1784d522f475Smrg
178501037d57Smrg    TRACE(("Getting %s (type=%ld, length=%ld)\n", name, (long int) type, len));
1786d522f475Smrg    for (cp = line; cp < line + len; cp++) {
1787956cc18dSsnj	TRACE(("[%d:%lu]", (int) (cp + 1 - line), len));
1788d522f475Smrg	if (isprint(*cp)) {
1789d522f475Smrg	    TRACE(("%c\n", *cp));
1790d522f475Smrg	} else {
1791d522f475Smrg	    TRACE(("\\x%02x\n", *cp));
1792d522f475Smrg	}
1793d522f475Smrg    }
1794d522f475Smrg}
1795d522f475Smrg#else
1796d522f475Smrg#define GettingSelection(dpy,type,line,len)	/* nothing */
1797d522f475Smrg#endif
1798d522f475Smrg
1799d522f475Smrg#ifdef VMS
1800d522f475Smrg#  define tty_vwrite(pty,lag,l)		tt_write(lag,l)
1801d522f475Smrg#else /* !( VMS ) */
1802d522f475Smrg#  define tty_vwrite(pty,lag,l)		v_write(pty,lag,l)
1803d522f475Smrg#endif /* defined VMS */
1804d522f475Smrg
1805d522f475Smrg#if OPT_PASTE64
1806d522f475Smrg/* Return base64 code character given 6-bit number */
1807d522f475Smrgstatic const char base64_code[] = "\
1808d522f475SmrgABCDEFGHIJKLMNOPQRSTUVWXYZ\
1809d522f475Smrgabcdefghijklmnopqrstuvwxyz\
1810d522f475Smrg0123456789+/";
1811d522f475Smrgstatic void
1812e0a2b6dfSmrgbase64_flush(TScreen *screen)
1813d522f475Smrg{
1814d522f475Smrg    Char x;
181501037d57Smrg
181601037d57Smrg    TRACE(("base64_flush count %d, pad %d (%d)\n",
181701037d57Smrg	   screen->base64_count,
181801037d57Smrg	   screen->base64_pad,
181901037d57Smrg	   screen->base64_pad & 3));
182001037d57Smrg
1821d522f475Smrg    switch (screen->base64_count) {
1822d522f475Smrg    case 0:
1823d522f475Smrg	break;
1824d522f475Smrg    case 2:
18252eaa94a1Schristos	x = CharOf(base64_code[screen->base64_accu << 4]);
1826d522f475Smrg	tty_vwrite(screen->respond, &x, 1);
1827d522f475Smrg	break;
1828d522f475Smrg    case 4:
18292eaa94a1Schristos	x = CharOf(base64_code[screen->base64_accu << 2]);
1830d522f475Smrg	tty_vwrite(screen->respond, &x, 1);
1831d522f475Smrg	break;
1832d522f475Smrg    }
183301037d57Smrg    if (screen->base64_pad & 3) {
1834d522f475Smrg	tty_vwrite(screen->respond,
1835492d43a5Smrg		   (const Char *) "===",
183601037d57Smrg		   (unsigned) (3 - (screen->base64_pad & 3)));
183701037d57Smrg    }
1838d522f475Smrg    screen->base64_count = 0;
1839d522f475Smrg    screen->base64_accu = 0;
1840d522f475Smrg    screen->base64_pad = 0;
1841d522f475Smrg}
1842d522f475Smrg#endif /* OPT_PASTE64 */
1843d522f475Smrg
1844e0a2b6dfSmrg/*
1845e0a2b6dfSmrg * Translate ISO-8859-1 or UTF-8 data to NRCS.
1846e0a2b6dfSmrg */
1847d522f475Smrgstatic void
1848e0a2b6dfSmrgToNational(TScreen *screen, Char *buffer, unsigned *length)
1849e0a2b6dfSmrg{
1850e0a2b6dfSmrg    int gsetL = screen->gsets[screen->curgl];
1851e0a2b6dfSmrg    int gsetR = screen->gsets[screen->curgr];
1852e0a2b6dfSmrg
1853e0a2b6dfSmrg#if OPT_WIDE_CHARS
1854e0a2b6dfSmrg    if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
18552e4f8982Smrg	Char *p;
1856e0a2b6dfSmrg	PtyData *data = TypeXtMallocX(PtyData, *length);
1857e0a2b6dfSmrg
1858e0a2b6dfSmrg	memset(data, 0, sizeof(*data));
1859e0a2b6dfSmrg	data->next = data->buffer;
1860e0a2b6dfSmrg	data->last = data->buffer + *length;
1861e0a2b6dfSmrg	memcpy(data->buffer, buffer, (size_t) *length);
1862e0a2b6dfSmrg	p = buffer;
1863e0a2b6dfSmrg	while (data->next < data->last) {
18642e4f8982Smrg	    unsigned chr, out, gl, gr;
18652e4f8982Smrg
1866894e0ac8Smrg	    if (!decodeUtf8(screen, data)) {
1867e0a2b6dfSmrg		data->utf_size = 1;
1868e0a2b6dfSmrg		data->utf_data = data->next[0];
1869e0a2b6dfSmrg	    }
1870e0a2b6dfSmrg	    data->next += data->utf_size;
1871e0a2b6dfSmrg	    chr = data->utf_data;
1872e0a2b6dfSmrg	    out = chr;
1873e0a2b6dfSmrg	    if ((gl = xtermCharSetIn(screen, chr, gsetL)) != chr) {
1874e0a2b6dfSmrg		out = gl;
1875e0a2b6dfSmrg	    } else if ((gr = xtermCharSetIn(screen, chr, gsetR)) != chr) {
1876e0a2b6dfSmrg		out = gr;
1877e0a2b6dfSmrg	    }
1878e0a2b6dfSmrg	    *p++ = (Char) ((out < 256) ? out : ' ');
1879e0a2b6dfSmrg	}
1880e0a2b6dfSmrg	*length = (unsigned) (p - buffer);
1881e0a2b6dfSmrg	free(data);
1882e0a2b6dfSmrg    } else
1883e0a2b6dfSmrg#endif
1884e0a2b6dfSmrg    {
18852e4f8982Smrg	Char *p;
18862e4f8982Smrg
1887e0a2b6dfSmrg	for (p = buffer; (int) (p - buffer) < (int) *length; ++p) {
18882e4f8982Smrg	    unsigned gl, gr;
18892e4f8982Smrg	    unsigned chr = *p;
18902e4f8982Smrg	    unsigned out = chr;
1891e0a2b6dfSmrg	    if ((gl = xtermCharSetIn(screen, chr, gsetL)) != chr) {
1892e0a2b6dfSmrg		out = gl;
1893e0a2b6dfSmrg	    } else if ((gr = xtermCharSetIn(screen, chr, gsetR)) != chr) {
1894e0a2b6dfSmrg		out = gr;
1895e0a2b6dfSmrg	    }
1896e0a2b6dfSmrg	    *p = (Char) out;
1897e0a2b6dfSmrg	}
1898e0a2b6dfSmrg    }
1899e0a2b6dfSmrg}
1900e0a2b6dfSmrg
1901e0a2b6dfSmrgstatic void
1902e0a2b6dfSmrg_qWriteSelectionData(XtermWidget xw, Char *lag, unsigned length)
1903d522f475Smrg{
19040bd37d32Smrg    TScreen *screen = TScreenOf(xw);
19050bd37d32Smrg
1906e0a2b6dfSmrg    /*
1907e0a2b6dfSmrg     * If we are pasting into a window which is using NRCS, we want to map
1908e0a2b6dfSmrg     * the text from the normal encoding (ISO-8859-1 or UTF-8) into the coding
1909e0a2b6dfSmrg     * that an application would use to write characters with NRCS.
1910e0a2b6dfSmrg     *
1911e0a2b6dfSmrg     * TODO: handle conversion from UTF-8, and adjust length.  This can be done
1912e0a2b6dfSmrg     * in the same buffer because the target is always 8-bit.
1913e0a2b6dfSmrg     */
1914e0a2b6dfSmrg    if ((xw->flags & NATIONAL) && (length != 0)) {
1915e0a2b6dfSmrg	ToNational(screen, lag, &length);
1916e0a2b6dfSmrg    }
1917d522f475Smrg#if OPT_PASTE64
1918d522f475Smrg    if (screen->base64_paste) {
1919d522f475Smrg	/* Send data as base64 */
1920d522f475Smrg	Char *p = lag;
1921d522f475Smrg	Char buf[64];
1922d522f475Smrg	unsigned x = 0;
19230bd37d32Smrg
192401037d57Smrg	TRACE(("convert to base64 %d:%s\n", length, visibleChars(p, length)));
192501037d57Smrg
19260bd37d32Smrg	/*
19270bd37d32Smrg	 * Handle the case where the selection is from _this_ xterm, which
19280bd37d32Smrg	 * puts part of the reply in the buffer before the selection callback
19290bd37d32Smrg	 * happens.
19300bd37d32Smrg	 */
19310bd37d32Smrg	if (screen->base64_paste && screen->unparse_len) {
19320bd37d32Smrg	    unparse_end(xw);
19330bd37d32Smrg	}
1934d522f475Smrg	while (length--) {
1935d522f475Smrg	    switch (screen->base64_count) {
1936d522f475Smrg	    case 0:
19372eaa94a1Schristos		buf[x++] = CharOf(base64_code[*p >> 2]);
19382eaa94a1Schristos		screen->base64_accu = (unsigned) (*p & 0x3);
1939d522f475Smrg		screen->base64_count = 2;
1940d522f475Smrg		++p;
1941d522f475Smrg		break;
1942d522f475Smrg	    case 2:
19432eaa94a1Schristos		buf[x++] = CharOf(base64_code[(screen->base64_accu << 4) +
19442eaa94a1Schristos					      (*p >> 4)]);
19452eaa94a1Schristos		screen->base64_accu = (unsigned) (*p & 0xF);
1946d522f475Smrg		screen->base64_count = 4;
1947d522f475Smrg		++p;
1948d522f475Smrg		break;
1949d522f475Smrg	    case 4:
19502eaa94a1Schristos		buf[x++] = CharOf(base64_code[(screen->base64_accu << 2) +
19512eaa94a1Schristos					      (*p >> 6)]);
19522eaa94a1Schristos		buf[x++] = CharOf(base64_code[*p & 0x3F]);
1953d522f475Smrg		screen->base64_accu = 0;
1954d522f475Smrg		screen->base64_count = 0;
1955d522f475Smrg		++p;
1956d522f475Smrg		break;
1957d522f475Smrg	    }
1958d522f475Smrg	    if (x >= 63) {
1959d522f475Smrg		/* Write 63 or 64 characters */
1960d522f475Smrg		screen->base64_pad += x;
196101037d57Smrg		TRACE(("writing base64 interim %s\n", visibleChars(buf, x)));
1962d522f475Smrg		tty_vwrite(screen->respond, buf, x);
1963d522f475Smrg		x = 0;
1964d522f475Smrg	    }
1965d522f475Smrg	}
1966d522f475Smrg	if (x != 0) {
1967d522f475Smrg	    screen->base64_pad += x;
196801037d57Smrg	    TRACE(("writing base64 finish %s\n", visibleChars(buf, x)));
1969d522f475Smrg	    tty_vwrite(screen->respond, buf, x);
1970d522f475Smrg	}
1971d522f475Smrg    } else
1972d522f475Smrg#endif /* OPT_PASTE64 */
1973d522f475Smrg#if OPT_READLINE
1974d522f475Smrg    if (SCREEN_FLAG(screen, paste_quotes)) {
1975d522f475Smrg	while (length--) {
1976492d43a5Smrg	    tty_vwrite(screen->respond, (const Char *) "\026", 1);	/* Control-V */
1977d522f475Smrg	    tty_vwrite(screen->respond, lag++, 1);
1978d522f475Smrg	}
1979d522f475Smrg    } else
1980d522f475Smrg#endif
198101037d57Smrg    {
198201037d57Smrg	TRACE(("writing base64 padding %s\n", visibleChars(lag, length)));
1983d522f475Smrg	tty_vwrite(screen->respond, lag, length);
198401037d57Smrg    }
1985d522f475Smrg}
1986d522f475Smrg
1987d522f475Smrgstatic void
1988e0a2b6dfSmrg_WriteSelectionData(XtermWidget xw, Char *line, size_t length)
1989d522f475Smrg{
1990d522f475Smrg    /* Write data to pty a line at a time. */
1991d522f475Smrg    /* Doing this one line at a time may no longer be necessary
1992d522f475Smrg       because v_write has been re-written. */
1993d522f475Smrg
19942e4f8982Smrg#if OPT_PASTE64
19950bd37d32Smrg    TScreen *screen = TScreenOf(xw);
19962e4f8982Smrg#endif
1997d522f475Smrg    Char *lag, *end;
1998d522f475Smrg
1999d522f475Smrg    /* in the VMS version, if tt_pasting isn't set to True then qio
2000d522f475Smrg       reads aren't blocked and an infinite loop is entered, where the
2001d522f475Smrg       pasted text shows up as new input, goes in again, shows up
2002d522f475Smrg       again, ad nauseum. */
2003d522f475Smrg#ifdef VMS
2004d522f475Smrg    tt_pasting = True;
2005d522f475Smrg#endif
2006d522f475Smrg
2007d522f475Smrg    end = &line[length];
2008d522f475Smrg    lag = line;
2009d522f475Smrg
2010d522f475Smrg#if OPT_PASTE64
2011d522f475Smrg    if (screen->base64_paste) {
20120bd37d32Smrg	_qWriteSelectionData(xw, lag, (unsigned) (end - lag));
2013d522f475Smrg	base64_flush(screen);
2014d522f475Smrg    } else
2015d522f475Smrg#endif
2016d522f475Smrg    {
2017d522f475Smrg	if (!SCREEN_FLAG(screen, paste_literal_nl)) {
2018d522f475Smrg	    Char *cp;
2019d522f475Smrg	    for (cp = line; cp != end; cp++) {
2020d522f475Smrg		if (*cp == '\n') {
2021d522f475Smrg		    *cp = '\r';
20220bd37d32Smrg		    _qWriteSelectionData(xw, lag, (unsigned) (cp - lag + 1));
2023d522f475Smrg		    lag = cp + 1;
2024d522f475Smrg		}
2025d522f475Smrg	    }
2026d522f475Smrg	}
2027d522f475Smrg
2028d522f475Smrg	if (lag != end) {
20290bd37d32Smrg	    _qWriteSelectionData(xw, lag, (unsigned) (end - lag));
2030d522f475Smrg	}
2031d522f475Smrg    }
2032d522f475Smrg#ifdef VMS
2033d522f475Smrg    tt_pasting = False;
2034d522f475Smrg    tt_start_read();		/* reenable reads or a character may be lost */
2035d522f475Smrg#endif
2036d522f475Smrg}
2037d522f475Smrg
2038d522f475Smrg#if OPT_READLINE
2039d522f475Smrgstatic void
2040e0a2b6dfSmrg_WriteKey(TScreen *screen, const Char *in)
2041d522f475Smrg{
2042d522f475Smrg    Char line[16];
2043d522f475Smrg    unsigned count = 0;
2044492d43a5Smrg    size_t length = strlen((const char *) in);
2045d522f475Smrg
2046d522f475Smrg    if (screen->control_eight_bits) {
2047d522f475Smrg	line[count++] = ANSI_CSI;
2048d522f475Smrg    } else {
2049d522f475Smrg	line[count++] = ANSI_ESC;
2050d522f475Smrg	line[count++] = '[';
2051d522f475Smrg    }
2052d522f475Smrg    while (length--)
2053d522f475Smrg	line[count++] = *in++;
2054d522f475Smrg    line[count++] = '~';
2055d522f475Smrg    tty_vwrite(screen->respond, line, count);
2056d522f475Smrg}
2057d522f475Smrg#endif /* OPT_READLINE */
2058d522f475Smrg
20590bd37d32Smrg/*
20600bd37d32Smrg * Unless enabled by the user, strip control characters other than formatting.
20610bd37d32Smrg */
20620bd37d32Smrgstatic size_t
20630bd37d32SmrgremoveControls(XtermWidget xw, char *value)
20640bd37d32Smrg{
20650bd37d32Smrg    TScreen *screen = TScreenOf(xw);
20660bd37d32Smrg    size_t dst = 0;
20670bd37d32Smrg
20680bd37d32Smrg    if (screen->allowPasteControls) {
20690bd37d32Smrg	dst = strlen(value);
20700bd37d32Smrg    } else {
20712e4f8982Smrg	size_t src = 0;
20720bd37d32Smrg	while ((value[dst] = value[src]) != '\0') {
20730bd37d32Smrg	    int ch = CharOf(value[src++]);
20740bd37d32Smrg	    if (ch < 32) {
20750bd37d32Smrg		switch (ch) {
20760bd37d32Smrg		case '\b':
20770bd37d32Smrg		case '\t':
20780bd37d32Smrg		case '\n':
20790bd37d32Smrg		case '\r':
20800bd37d32Smrg		    ++dst;
20810bd37d32Smrg		    break;
20820bd37d32Smrg		default:
20830bd37d32Smrg		    continue;
20840bd37d32Smrg		}
20850bd37d32Smrg	    }
20860bd37d32Smrg#if OPT_WIDE_CHARS
2087e0a2b6dfSmrg	    else if (screen->utf8_inparse || screen->utf8_nrc_mode)
20880bd37d32Smrg		++dst;
20890bd37d32Smrg#endif
20900bd37d32Smrg#if OPT_C1_PRINT || OPT_WIDE_CHARS
20910bd37d32Smrg	    else if (screen->c1_printable)
20920bd37d32Smrg		++dst;
20930bd37d32Smrg#endif
20940bd37d32Smrg	    else if (ch >= 128 && ch < 160)
20950bd37d32Smrg		continue;
20960bd37d32Smrg	    else
20970bd37d32Smrg		++dst;
20980bd37d32Smrg	}
20990bd37d32Smrg    }
21000bd37d32Smrg    return dst;
21010bd37d32Smrg}
21020bd37d32Smrg
2103d522f475Smrg/* SelectionReceived: stuff received selection text into pty */
2104d522f475Smrg
2105d522f475Smrg/* ARGSUSED */
2106d522f475Smrgstatic void
2107d522f475SmrgSelectionReceived(Widget w,
2108d522f475Smrg		  XtPointer client_data,
2109894e0ac8Smrg		  Atom *selection GCC_UNUSED,
2110894e0ac8Smrg		  Atom *type,
2111d522f475Smrg		  XtPointer value,
2112d522f475Smrg		  unsigned long *length,
2113d522f475Smrg		  int *format)
2114d522f475Smrg{
2115d522f475Smrg    char **text_list = NULL;
21162e4f8982Smrg    int text_list_count = 0;
2117d522f475Smrg    XTextProperty text_prop;
2118d522f475Smrg    TScreen *screen;
2119d522f475Smrg    Display *dpy;
2120d522f475Smrg#if OPT_TRACE && OPT_WIDE_CHARS
2121d522f475Smrg    Char *line = (Char *) value;
2122d522f475Smrg#endif
2123d522f475Smrg
2124956cc18dSsnj    XtermWidget xw;
2125956cc18dSsnj
2126956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0)
2127d522f475Smrg	return;
2128956cc18dSsnj
2129956cc18dSsnj    screen = TScreenOf(xw);
2130d522f475Smrg    dpy = XtDisplay(w);
2131d522f475Smrg
2132d522f475Smrg    if (*type == 0		/*XT_CONVERT_FAIL */
2133d522f475Smrg	|| *length == 0
213401037d57Smrg	|| value == NULL) {
213501037d57Smrg	TRACE(("...no data to convert\n"));
2136d522f475Smrg	goto fail;
213701037d57Smrg    }
2138d522f475Smrg
2139d522f475Smrg    text_prop.value = (unsigned char *) value;
2140d522f475Smrg    text_prop.encoding = *type;
2141d522f475Smrg    text_prop.format = *format;
2142d522f475Smrg    text_prop.nitems = *length;
2143d522f475Smrg
214401037d57Smrg    TRACE(("SelectionReceived %s %s format %d, nitems %ld\n",
2145913cc679Smrg	   TraceAtomName(screen->display, *selection),
2146956cc18dSsnj	   visibleSelectionTarget(dpy, text_prop.encoding),
2147956cc18dSsnj	   text_prop.format,
2148956cc18dSsnj	   text_prop.nitems));
2149956cc18dSsnj
2150d522f475Smrg#if OPT_WIDE_CHARS
2151e39b573cSmrg    if (XSupportsLocale() && screen->wide_chars) {
2152d522f475Smrg	if (*type == XA_UTF8_STRING(dpy) ||
2153d522f475Smrg	    *type == XA_STRING ||
2154d522f475Smrg	    *type == XA_COMPOUND_TEXT(dpy)) {
2155d522f475Smrg	    GettingSelection(dpy, *type, line, *length);
2156d522f475Smrg	    if (Xutf8TextPropertyToTextList(dpy, &text_prop,
2157d522f475Smrg					    &text_list,
2158d522f475Smrg					    &text_list_count) < 0) {
2159e39b573cSmrg		TRACE(("default Xutf8 Conversion failed\n"));
2160d522f475Smrg		text_list = NULL;
2161d522f475Smrg	    }
2162d522f475Smrg	}
2163d522f475Smrg    } else
2164d522f475Smrg#endif /* OPT_WIDE_CHARS */
2165d522f475Smrg    {
2166d522f475Smrg	/* Convert the selection to locale's multibyte encoding. */
2167d522f475Smrg
2168d522f475Smrg	if (*type == XA_UTF8_STRING(dpy) ||
2169d522f475Smrg	    *type == XA_STRING ||
2170d522f475Smrg	    *type == XA_COMPOUND_TEXT(dpy)) {
2171d522f475Smrg	    Status rc;
2172d522f475Smrg
2173d522f475Smrg	    GettingSelection(dpy, *type, line, *length);
2174d522f475Smrg
2175d522f475Smrg#if OPT_WIDE_CHARS
2176d522f475Smrg	    if (*type == XA_UTF8_STRING(dpy) &&
2177d522f475Smrg		!(screen->wide_chars || screen->c1_printable)) {
217820d2c4d2Smrg		rc = xtermUtf8ToTextList(xw, &text_prop,
217920d2c4d2Smrg					 &text_list, &text_list_count);
2180d522f475Smrg	    } else
2181d522f475Smrg#endif
2182e39b573cSmrg	    if (*type == XA_STRING && (!XSupportsLocale() || screen->brokenSelections)) {
2183d522f475Smrg		rc = XTextPropertyToStringList(&text_prop,
2184d522f475Smrg					       &text_list, &text_list_count);
2185d522f475Smrg	    } else {
2186d522f475Smrg		rc = XmbTextPropertyToTextList(dpy, &text_prop,
2187d522f475Smrg					       &text_list,
2188d522f475Smrg					       &text_list_count);
2189d522f475Smrg	    }
2190d522f475Smrg	    if (rc < 0) {
2191d522f475Smrg		TRACE(("Conversion failed\n"));
2192d522f475Smrg		text_list = NULL;
2193d522f475Smrg	    }
2194d522f475Smrg	}
2195d522f475Smrg    }
2196d522f475Smrg
2197d522f475Smrg    if (text_list != NULL && text_list_count != 0) {
2198d522f475Smrg	int i;
2199d522f475Smrg
2200d522f475Smrg#if OPT_PASTE64
2201d522f475Smrg	if (screen->base64_paste) {
2202a1f3da82Smrg	    /* EMPTY */ ;
2203d522f475Smrg	} else
2204d522f475Smrg#endif
2205d522f475Smrg#if OPT_READLINE
2206d522f475Smrg	if (SCREEN_FLAG(screen, paste_brackets)) {
2207492d43a5Smrg	    _WriteKey(screen, (const Char *) "200");
2208d522f475Smrg	}
2209d522f475Smrg#endif
2210d522f475Smrg	for (i = 0; i < text_list_count; i++) {
22110bd37d32Smrg	    size_t len = removeControls(xw, text_list[i]);
221201037d57Smrg
22130bd37d32Smrg	    if (screen->selectToBuffer) {
221401037d57Smrg		InternalSelect *mydata = &(screen->internal_select);
221501037d57Smrg		size_t have = (mydata->buffer
221601037d57Smrg			       ? strlen(mydata->buffer)
22170bd37d32Smrg			       : 0);
22180bd37d32Smrg		size_t need = have + len + 1;
221901037d57Smrg		char *buffer = realloc(mydata->buffer, need);
222001037d57Smrg
222101037d57Smrg		screen->selectToBuffer = False;
222201037d57Smrg#if OPT_PASTE64
222301037d57Smrg		screen->base64_paste = mydata->base64_paste;
222401037d57Smrg#endif
222501037d57Smrg#if OPT_READLINE
222601037d57Smrg		screen->paste_brackets = mydata->paste_brackets;
222701037d57Smrg#endif
22280bd37d32Smrg		if (buffer != 0) {
22290bd37d32Smrg		    strcpy(buffer + have, text_list[i]);
223001037d57Smrg		    mydata->buffer = buffer;
22310bd37d32Smrg		}
223201037d57Smrg		TRACE(("FormatSelect %d.%d .. %d.%d %s\n",
223301037d57Smrg		       screen->startSel.row,
223401037d57Smrg		       screen->startSel.col,
223501037d57Smrg		       screen->endSel.row,
223601037d57Smrg		       screen->endSel.col,
223701037d57Smrg		       mydata->buffer));
223801037d57Smrg		mydata->format_select(w, mydata->format, mydata->buffer,
223901037d57Smrg				      &(screen->startSel),
224001037d57Smrg				      &(screen->endSel));
224101037d57Smrg
224201037d57Smrg		free(mydata->format);
224301037d57Smrg		free(mydata->buffer);
224401037d57Smrg		memset(mydata, 0, sizeof(*mydata));
22450bd37d32Smrg	    } else {
22460bd37d32Smrg		_WriteSelectionData(xw, (Char *) text_list[i], len);
22470bd37d32Smrg	    }
2248d522f475Smrg	}
2249d522f475Smrg#if OPT_PASTE64
2250d522f475Smrg	if (screen->base64_paste) {
2251956cc18dSsnj	    FinishPaste64(xw);
2252d522f475Smrg	} else
2253d522f475Smrg#endif
2254d522f475Smrg#if OPT_READLINE
2255d522f475Smrg	if (SCREEN_FLAG(screen, paste_brackets)) {
2256492d43a5Smrg	    _WriteKey(screen, (const Char *) "201");
2257d522f475Smrg	}
2258d522f475Smrg#endif
2259d522f475Smrg	XFreeStringList(text_list);
226001037d57Smrg    } else {
226101037d57Smrg	TRACE(("...empty text-list\n"));
2262d522f475Smrg	goto fail;
226301037d57Smrg    }
2264d522f475Smrg
2265d522f475Smrg    XtFree((char *) client_data);
2266d522f475Smrg    XtFree((char *) value);
2267d522f475Smrg
2268d522f475Smrg    return;
2269d522f475Smrg
2270d522f475Smrg  fail:
2271d522f475Smrg    if (client_data != 0) {
2272d522f475Smrg	struct _SelectionList *list = (struct _SelectionList *) client_data;
2273956cc18dSsnj
2274956cc18dSsnj	TRACE(("SelectionReceived ->xtermGetSelection\n"));
2275d522f475Smrg	xtermGetSelection(w, list->time,
2276d522f475Smrg			  list->params, list->count, list->targets);
2277d522f475Smrg	XtFree((char *) client_data);
2278d522f475Smrg#if OPT_PASTE64
2279d522f475Smrg    } else {
2280956cc18dSsnj	FinishPaste64(xw);
2281d522f475Smrg#endif
2282d522f475Smrg    }
2283d522f475Smrg    return;
2284d522f475Smrg}
2285d522f475Smrg
2286d522f475Smrgvoid
2287d522f475SmrgHandleInsertSelection(Widget w,
2288894e0ac8Smrg		      XEvent *event,	/* assumed to be XButtonEvent* */
2289e0a2b6dfSmrg		      String *params,	/* selections in precedence order */
2290d522f475Smrg		      Cardinal *num_params)
2291d522f475Smrg{
2292956cc18dSsnj    XtermWidget xw;
2293d522f475Smrg
2294956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
22950bd37d32Smrg	TRACE(("HandleInsertSelection\n"));
2296d522f475Smrg	if (!SendMousePosition(xw, event)) {
2297d522f475Smrg#if OPT_READLINE
2298d522f475Smrg	    int ldelta;
2299956cc18dSsnj	    TScreen *screen = TScreenOf(xw);
2300492d43a5Smrg	    if (IsBtnEvent(event)
2301d522f475Smrg	    /* Disable on Shift-mouse, including the application-mouse modes */
2302492d43a5Smrg		&& !(KeyModifiers(event) & ShiftMask)
2303913cc679Smrg		&& (okSendMousePos(xw) == MOUSE_OFF)
2304d522f475Smrg		&& SCREEN_FLAG(screen, paste_moves)
2305d522f475Smrg		&& rowOnCurrentLine(screen, eventRow(screen, event), &ldelta))
2306d522f475Smrg		ReadLineMovePoint(screen, eventColBetween(screen, event), ldelta);
2307d522f475Smrg#endif /* OPT_READLINE */
2308d522f475Smrg
2309d522f475Smrg	    xtermGetSelection(w, event->xbutton.time, params, *num_params, NULL);
2310d522f475Smrg	}
2311d522f475Smrg    }
2312d522f475Smrg}
2313d522f475Smrg
2314d522f475Smrgstatic SelectUnit
2315956cc18dSsnjEvalSelectUnit(XtermWidget xw,
2316d522f475Smrg	       Time buttonDownTime,
2317d522f475Smrg	       SelectUnit defaultUnit,
2318d522f475Smrg	       unsigned int button)
2319d522f475Smrg{
2320956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2321d522f475Smrg    SelectUnit result;
2322d522f475Smrg    int delta;
2323d522f475Smrg
2324d522f475Smrg    if (button != screen->lastButton) {
232520d2c4d2Smrg	delta = screen->multiClickTime + 1;
2326d522f475Smrg    } else if (screen->lastButtonUpTime == (Time) 0) {
2327d522f475Smrg	/* first time and once in a blue moon */
2328d522f475Smrg	delta = screen->multiClickTime + 1;
2329d522f475Smrg    } else if (buttonDownTime > screen->lastButtonUpTime) {
2330d522f475Smrg	/* most of the time */
23312eaa94a1Schristos	delta = (int) (buttonDownTime - screen->lastButtonUpTime);
2332d522f475Smrg    } else {
2333d522f475Smrg	/* time has rolled over since lastButtonUpTime */
23342eaa94a1Schristos	delta = (int) ((((Time) ~ 0) - screen->lastButtonUpTime) + buttonDownTime);
2335d522f475Smrg    }
2336d522f475Smrg
2337d522f475Smrg    if (delta > screen->multiClickTime) {
2338d522f475Smrg	screen->numberOfClicks = 1;
2339d522f475Smrg	result = defaultUnit;
2340d522f475Smrg    } else {
2341d522f475Smrg	result = screen->selectMap[screen->numberOfClicks % screen->maxClicks];
2342d522f475Smrg	screen->numberOfClicks += 1;
2343d522f475Smrg    }
2344d522f475Smrg    TRACE(("EvalSelectUnit(%d) = %d\n", screen->numberOfClicks, result));
2345d522f475Smrg    return result;
2346d522f475Smrg}
2347d522f475Smrg
2348d522f475Smrgstatic void
2349d522f475Smrgdo_select_start(XtermWidget xw,
2350894e0ac8Smrg		XEvent *event,	/* must be XButtonEvent* */
2351e0a2b6dfSmrg		CELL *cell)
2352d522f475Smrg{
2353956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2354d522f475Smrg
2355d522f475Smrg    if (SendMousePosition(xw, event))
2356d522f475Smrg	return;
2357956cc18dSsnj    screen->selectUnit = EvalSelectUnit(xw,
2358d522f475Smrg					event->xbutton.time,
2359d522f475Smrg					Select_CHAR,
2360d522f475Smrg					event->xbutton.button);
2361d522f475Smrg    screen->replyToEmacs = False;
2362d522f475Smrg
2363d522f475Smrg#if OPT_READLINE
2364d522f475Smrg    lastButtonDownTime = event->xbutton.time;
2365d522f475Smrg#endif
2366d522f475Smrg
2367d522f475Smrg    StartSelect(xw, cell);
2368d522f475Smrg}
2369d522f475Smrg
2370d522f475Smrg/* ARGSUSED */
2371d522f475Smrgvoid
2372d522f475SmrgHandleSelectStart(Widget w,
2373894e0ac8Smrg		  XEvent *event,	/* must be XButtonEvent* */
2374e0a2b6dfSmrg		  String *params GCC_UNUSED,
2375d522f475Smrg		  Cardinal *num_params GCC_UNUSED)
2376d522f475Smrg{
2377956cc18dSsnj    XtermWidget xw;
2378956cc18dSsnj
2379956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
2380956cc18dSsnj	TScreen *screen = TScreenOf(xw);
2381d522f475Smrg	CELL cell;
2382d522f475Smrg
23830bd37d32Smrg	TRACE(("HandleSelectStart\n"));
2384d522f475Smrg	screen->firstValidRow = 0;
2385d522f475Smrg	screen->lastValidRow = screen->max_row;
2386d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
2387d522f475Smrg
2388d522f475Smrg#if OPT_READLINE
2389d522f475Smrg	ExtendingSelection = 0;
2390d522f475Smrg#endif
2391d522f475Smrg
2392d522f475Smrg	do_select_start(xw, event, &cell);
2393d522f475Smrg    }
2394d522f475Smrg}
2395d522f475Smrg
2396d522f475Smrg/* ARGSUSED */
2397d522f475Smrgvoid
2398d522f475SmrgHandleKeyboardSelectStart(Widget w,
2399894e0ac8Smrg			  XEvent *event,	/* must be XButtonEvent* */
2400e0a2b6dfSmrg			  String *params GCC_UNUSED,
2401d522f475Smrg			  Cardinal *num_params GCC_UNUSED)
2402d522f475Smrg{
2403956cc18dSsnj    XtermWidget xw;
2404956cc18dSsnj
2405956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
2406956cc18dSsnj	TScreen *screen = TScreenOf(xw);
24070bd37d32Smrg
24080bd37d32Smrg	TRACE(("HandleKeyboardSelectStart\n"));
2409d522f475Smrg	do_select_start(xw, event, &screen->cursorp);
2410d522f475Smrg    }
2411d522f475Smrg}
2412d522f475Smrg
2413d522f475Smrgstatic void
2414894e0ac8SmrgTrackDown(XtermWidget xw, XButtonEvent *event)
2415d522f475Smrg{
2416956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2417d522f475Smrg    CELL cell;
2418d522f475Smrg
2419956cc18dSsnj    screen->selectUnit = EvalSelectUnit(xw,
2420d522f475Smrg					event->time,
2421d522f475Smrg					Select_CHAR,
2422d522f475Smrg					event->button);
2423d522f475Smrg    if (screen->numberOfClicks > 1) {
2424d522f475Smrg	PointToCELL(screen, event->y, event->x, &cell);
2425d522f475Smrg	screen->replyToEmacs = True;
2426d522f475Smrg	StartSelect(xw, &cell);
2427d522f475Smrg    } else {
2428d522f475Smrg	screen->waitingForTrackInfo = True;
2429492d43a5Smrg	EditorButton(xw, event);
2430d522f475Smrg    }
2431d522f475Smrg}
2432d522f475Smrg
2433d522f475Smrg#define boundsCheck(x)	if (x < 0) \
2434d522f475Smrg			    x = 0; \
2435d522f475Smrg			else if (x >= screen->max_row) \
2436d522f475Smrg			    x = screen->max_row
2437d522f475Smrg
2438d522f475Smrgvoid
2439d522f475SmrgTrackMouse(XtermWidget xw,
2440d522f475Smrg	   int func,
2441e0a2b6dfSmrg	   CELL *start,
2442d522f475Smrg	   int firstrow,
2443d522f475Smrg	   int lastrow)
2444d522f475Smrg{
2445956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2446d522f475Smrg
2447d522f475Smrg    if (screen->waitingForTrackInfo) {	/* if Timed, ignore */
2448d522f475Smrg	screen->waitingForTrackInfo = False;
2449d522f475Smrg
2450d522f475Smrg	if (func != 0) {
2451d522f475Smrg	    CELL first = *start;
2452d522f475Smrg
2453d522f475Smrg	    boundsCheck(first.row);
2454d522f475Smrg	    boundsCheck(firstrow);
2455d522f475Smrg	    boundsCheck(lastrow);
2456d522f475Smrg	    screen->firstValidRow = firstrow;
2457d522f475Smrg	    screen->lastValidRow = lastrow;
2458d522f475Smrg	    screen->replyToEmacs = True;
2459d522f475Smrg	    StartSelect(xw, &first);
2460d522f475Smrg	}
2461d522f475Smrg    }
2462d522f475Smrg}
2463d522f475Smrg
2464d522f475Smrgstatic void
2465e0a2b6dfSmrgStartSelect(XtermWidget xw, const CELL *cell)
2466d522f475Smrg{
2467956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2468d522f475Smrg
2469d522f475Smrg    TRACE(("StartSelect row=%d, col=%d\n", cell->row, cell->col));
2470d522f475Smrg    if (screen->cursor_state)
2471d522f475Smrg	HideCursor();
2472d522f475Smrg    if (screen->numberOfClicks == 1) {
2473d522f475Smrg	/* set start of selection */
2474d522f475Smrg	screen->rawPos = *cell;
2475d522f475Smrg    }
2476d522f475Smrg    /* else use old values in rawPos */
2477d522f475Smrg    screen->saveStartR = screen->startExt = screen->rawPos;
2478d522f475Smrg    screen->saveEndR = screen->endExt = screen->rawPos;
2479d522f475Smrg    if (Coordinate(screen, cell) < Coordinate(screen, &(screen->rawPos))) {
2480d522f475Smrg	screen->eventMode = LEFTEXTENSION;
2481d522f475Smrg	screen->startExt = *cell;
2482d522f475Smrg    } else {
2483d522f475Smrg	screen->eventMode = RIGHTEXTENSION;
2484d522f475Smrg	screen->endExt = *cell;
2485d522f475Smrg    }
2486d522f475Smrg    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False);
2487d522f475Smrg}
2488d522f475Smrg
2489d522f475Smrgstatic void
2490d522f475SmrgEndExtend(XtermWidget xw,
2491894e0ac8Smrg	  XEvent *event,	/* must be XButtonEvent */
2492e0a2b6dfSmrg	  String *params,	/* selections */
2493d522f475Smrg	  Cardinal num_params,
2494d522f475Smrg	  Bool use_cursor_loc)
2495d522f475Smrg{
2496d522f475Smrg    CELL cell;
2497956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2498d522f475Smrg
2499d522f475Smrg    if (use_cursor_loc) {
2500d522f475Smrg	cell = screen->cursorp;
2501d522f475Smrg    } else {
2502d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
2503d522f475Smrg    }
2504d522f475Smrg    ExtendExtend(xw, &cell);
25052e4f8982Smrg
2506d522f475Smrg    screen->lastButtonUpTime = event->xbutton.time;
2507d522f475Smrg    screen->lastButton = event->xbutton.button;
25082e4f8982Smrg
2509d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel))) {
2510d522f475Smrg	if (screen->replyToEmacs) {
25112e4f8982Smrg	    Char line[64];
25122e4f8982Smrg	    unsigned count = 0;
25132e4f8982Smrg
2514d522f475Smrg	    if (screen->control_eight_bits) {
2515d522f475Smrg		line[count++] = ANSI_CSI;
2516d522f475Smrg	    } else {
2517d522f475Smrg		line[count++] = ANSI_ESC;
2518d522f475Smrg		line[count++] = '[';
2519d522f475Smrg	    }
2520d522f475Smrg	    if (isSameCELL(&(screen->rawPos), &(screen->startSel))
2521d522f475Smrg		&& isSameCELL(&cell, &(screen->endSel))) {
2522d522f475Smrg		/* Use short-form emacs select */
25230bd37d32Smrg
25240bd37d32Smrg		switch (screen->extend_coords) {
25250bd37d32Smrg		case 0:
25260bd37d32Smrg		case SET_EXT_MODE_MOUSE:
25270bd37d32Smrg		    line[count++] = 't';
25280bd37d32Smrg		    break;
25290bd37d32Smrg		case SET_SGR_EXT_MODE_MOUSE:
25300bd37d32Smrg		    line[count++] = '<';
25310bd37d32Smrg		    break;
25320bd37d32Smrg		}
25330bd37d32Smrg
2534492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->endSel.col);
25350bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
2536492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->endSel.row);
25370bd37d32Smrg
25380bd37d32Smrg		switch (screen->extend_coords) {
25390bd37d32Smrg		case SET_SGR_EXT_MODE_MOUSE:
25400bd37d32Smrg		case SET_URXVT_EXT_MODE_MOUSE:
25410bd37d32Smrg		    line[count++] = 't';
25420bd37d32Smrg		    break;
25430bd37d32Smrg		}
2544d522f475Smrg	    } else {
2545d522f475Smrg		/* long-form, specify everything */
25460bd37d32Smrg
25470bd37d32Smrg		switch (screen->extend_coords) {
25480bd37d32Smrg		case 0:
25490bd37d32Smrg		case SET_EXT_MODE_MOUSE:
25500bd37d32Smrg		    line[count++] = 'T';
25510bd37d32Smrg		    break;
25520bd37d32Smrg		case SET_SGR_EXT_MODE_MOUSE:
25530bd37d32Smrg		    line[count++] = '<';
25540bd37d32Smrg		    break;
25550bd37d32Smrg		}
25560bd37d32Smrg
2557492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->startSel.col);
25580bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
2559492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->startSel.row);
25600bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
2561492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->endSel.col);
25620bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
2563492d43a5Smrg		count = EmitMousePosition(screen, line, count, screen->endSel.row);
25640bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
2565492d43a5Smrg		count = EmitMousePosition(screen, line, count, cell.col);
25660bd37d32Smrg		count = EmitMousePositionSeparator(screen, line, count);
2567492d43a5Smrg		count = EmitMousePosition(screen, line, count, cell.row);
25680bd37d32Smrg
25690bd37d32Smrg		switch (screen->extend_coords) {
25700bd37d32Smrg		case SET_SGR_EXT_MODE_MOUSE:
25710bd37d32Smrg		case SET_URXVT_EXT_MODE_MOUSE:
25720bd37d32Smrg		    line[count++] = 'T';
25730bd37d32Smrg		    break;
25740bd37d32Smrg		}
2575d522f475Smrg	    }
2576d522f475Smrg	    v_write(screen->respond, line, count);
2577d522f475Smrg	    TrackText(xw, &zeroCELL, &zeroCELL);
2578d522f475Smrg	}
2579d522f475Smrg    }
2580d522f475Smrg    SelectSet(xw, event, params, num_params);
2581d522f475Smrg    screen->eventMode = NORMAL;
2582d522f475Smrg}
2583d522f475Smrg
2584d522f475Smrgvoid
2585d522f475SmrgHandleSelectSet(Widget w,
2586894e0ac8Smrg		XEvent *event,
2587e0a2b6dfSmrg		String *params,
2588d522f475Smrg		Cardinal *num_params)
2589d522f475Smrg{
2590956cc18dSsnj    XtermWidget xw;
2591956cc18dSsnj
2592956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
25930bd37d32Smrg	TRACE(("HandleSelectSet\n"));
2594956cc18dSsnj	SelectSet(xw, event, params, *num_params);
2595d522f475Smrg    }
2596d522f475Smrg}
2597d522f475Smrg
2598d522f475Smrg/* ARGSUSED */
2599d522f475Smrgstatic void
2600d522f475SmrgSelectSet(XtermWidget xw,
2601894e0ac8Smrg	  XEvent *event GCC_UNUSED,
2602e0a2b6dfSmrg	  String *params,
2603d522f475Smrg	  Cardinal num_params)
2604d522f475Smrg{
2605956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2606d522f475Smrg
2607d522f475Smrg    TRACE(("SelectSet\n"));
2608d522f475Smrg    /* Only do select stuff if non-null select */
2609d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel))) {
26100bd37d32Smrg	SaltTextAway(xw, &(screen->startSel), &(screen->endSel));
26110bd37d32Smrg	_OwnSelection(xw, params, num_params);
2612d522f475Smrg    } else {
26130bd37d32Smrg	ScrnDisownSelection(xw);
2614d522f475Smrg    }
2615d522f475Smrg}
2616d522f475Smrg
2617d522f475Smrg#define Abs(x)		((x) < 0 ? -(x) : (x))
2618d522f475Smrg
2619d522f475Smrg/* ARGSUSED */
2620d522f475Smrgstatic void
2621d522f475Smrgdo_start_extend(XtermWidget xw,
2622894e0ac8Smrg		XEvent *event,	/* must be XButtonEvent* */
2623e0a2b6dfSmrg		String *params GCC_UNUSED,
2624d522f475Smrg		Cardinal *num_params GCC_UNUSED,
2625d522f475Smrg		Bool use_cursor_loc)
2626d522f475Smrg{
2627956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2628d522f475Smrg    int coord;
2629d522f475Smrg    CELL cell;
2630d522f475Smrg
2631d522f475Smrg    if (SendMousePosition(xw, event))
2632d522f475Smrg	return;
2633d522f475Smrg
2634d522f475Smrg    screen->firstValidRow = 0;
2635d522f475Smrg    screen->lastValidRow = screen->max_row;
2636d522f475Smrg#if OPT_READLINE
2637492d43a5Smrg    if ((KeyModifiers(event) & ShiftMask)
2638d522f475Smrg	|| event->xbutton.button != Button3
2639d522f475Smrg	|| !(SCREEN_FLAG(screen, dclick3_deletes)))
2640d522f475Smrg#endif
2641956cc18dSsnj	screen->selectUnit = EvalSelectUnit(xw,
2642d522f475Smrg					    event->xbutton.time,
2643d522f475Smrg					    screen->selectUnit,
2644d522f475Smrg					    event->xbutton.button);
2645d522f475Smrg    screen->replyToEmacs = False;
2646d522f475Smrg
2647d522f475Smrg#if OPT_READLINE
2648d522f475Smrg    CheckSecondPress3(screen, event);
2649d522f475Smrg#endif
2650d522f475Smrg
2651d522f475Smrg    if (screen->numberOfClicks == 1
2652d522f475Smrg	|| (SCREEN_FLAG(screen, dclick3_deletes)	/* Dclick special */
2653492d43a5Smrg	    &&!(KeyModifiers(event) & ShiftMask))) {
2654d522f475Smrg	/* Save existing selection so we can reestablish it if the guy
2655d522f475Smrg	   extends past the other end of the selection */
2656d522f475Smrg	screen->saveStartR = screen->startExt = screen->startRaw;
2657d522f475Smrg	screen->saveEndR = screen->endExt = screen->endRaw;
2658d522f475Smrg    } else {
2659d522f475Smrg	/* He just needed the selection mode changed, use old values. */
2660d522f475Smrg	screen->startExt = screen->startRaw = screen->saveStartR;
2661d522f475Smrg	screen->endExt = screen->endRaw = screen->saveEndR;
2662d522f475Smrg    }
2663d522f475Smrg    if (use_cursor_loc) {
2664d522f475Smrg	cell = screen->cursorp;
2665d522f475Smrg    } else {
2666d522f475Smrg	PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
2667d522f475Smrg    }
2668d522f475Smrg    coord = Coordinate(screen, &cell);
2669d522f475Smrg
2670d522f475Smrg    if (Abs(coord - Coordinate(screen, &(screen->startSel)))
2671d522f475Smrg	< Abs(coord - Coordinate(screen, &(screen->endSel)))
2672d522f475Smrg	|| coord < Coordinate(screen, &(screen->startSel))) {
2673d522f475Smrg	/* point is close to left side of selection */
2674d522f475Smrg	screen->eventMode = LEFTEXTENSION;
2675d522f475Smrg	screen->startExt = cell;
2676d522f475Smrg    } else {
2677d522f475Smrg	/* point is close to left side of selection */
2678d522f475Smrg	screen->eventMode = RIGHTEXTENSION;
2679d522f475Smrg	screen->endExt = cell;
2680d522f475Smrg    }
2681d522f475Smrg    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), True);
2682d522f475Smrg
2683d522f475Smrg#if OPT_READLINE
2684d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel)))
2685d522f475Smrg	ExtendingSelection = 1;
2686d522f475Smrg#endif
2687d522f475Smrg}
2688d522f475Smrg
2689d522f475Smrgstatic void
2690e0a2b6dfSmrgExtendExtend(XtermWidget xw, const CELL *cell)
2691d522f475Smrg{
2692956cc18dSsnj    TScreen *screen = TScreenOf(xw);
2693d522f475Smrg    int coord = Coordinate(screen, cell);
2694d522f475Smrg
2695d522f475Smrg    TRACE(("ExtendExtend row=%d, col=%d\n", cell->row, cell->col));
2696d522f475Smrg    if (screen->eventMode == LEFTEXTENSION
2697d522f475Smrg	&& ((coord + (screen->selectUnit != Select_CHAR))
2698d522f475Smrg	    > Coordinate(screen, &(screen->endSel)))) {
2699d522f475Smrg	/* Whoops, he's changed his mind.  Do RIGHTEXTENSION */
2700d522f475Smrg	screen->eventMode = RIGHTEXTENSION;
2701d522f475Smrg	screen->startExt = screen->saveStartR;
2702d522f475Smrg    } else if (screen->eventMode == RIGHTEXTENSION
2703d522f475Smrg	       && coord < Coordinate(screen, &(screen->startSel))) {
2704d522f475Smrg	/* Whoops, he's changed his mind.  Do LEFTEXTENSION */
2705d522f475Smrg	screen->eventMode = LEFTEXTENSION;
2706d522f475Smrg	screen->endExt = screen->saveEndR;
2707d522f475Smrg    }
2708d522f475Smrg    if (screen->eventMode == LEFTEXTENSION) {
2709d522f475Smrg	screen->startExt = *cell;
2710d522f475Smrg    } else {
2711d522f475Smrg	screen->endExt = *cell;
2712d522f475Smrg    }
2713d522f475Smrg    ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False);
2714d522f475Smrg
2715d522f475Smrg#if OPT_READLINE
2716d522f475Smrg    if (!isSameCELL(&(screen->startSel), &(screen->endSel)))
2717d522f475Smrg	ExtendingSelection = 1;
2718d522f475Smrg#endif
2719d522f475Smrg}
2720d522f475Smrg
2721d522f475Smrgvoid
2722d522f475SmrgHandleStartExtend(Widget w,
2723894e0ac8Smrg		  XEvent *event,	/* must be XButtonEvent* */
2724e0a2b6dfSmrg		  String *params,	/* unused */
2725d522f475Smrg		  Cardinal *num_params)		/* unused */
2726d522f475Smrg{
2727956cc18dSsnj    XtermWidget xw;
2728956cc18dSsnj
2729956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
27300bd37d32Smrg	TRACE(("HandleStartExtend\n"));
2731956cc18dSsnj	do_start_extend(xw, event, params, num_params, False);
2732956cc18dSsnj    }
2733d522f475Smrg}
2734d522f475Smrg
2735d522f475Smrgvoid
2736d522f475SmrgHandleKeyboardStartExtend(Widget w,
2737894e0ac8Smrg			  XEvent *event,	/* must be XButtonEvent* */
2738e0a2b6dfSmrg			  String *params,	/* unused */
2739d522f475Smrg			  Cardinal *num_params)		/* unused */
2740d522f475Smrg{
2741956cc18dSsnj    XtermWidget xw;
2742956cc18dSsnj
2743956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
27440bd37d32Smrg	TRACE(("HandleKeyboardStartExtend\n"));
2745956cc18dSsnj	do_start_extend(xw, event, params, num_params, True);
2746956cc18dSsnj    }
2747d522f475Smrg}
2748d522f475Smrg
2749d522f475Smrgvoid
2750e0a2b6dfSmrgScrollSelection(TScreen *screen, int amount, Bool always)
2751d522f475Smrg{
2752d522f475Smrg    int minrow = INX2ROW(screen, -screen->savedlines);
2753d522f475Smrg    int maxrow = INX2ROW(screen, screen->max_row);
2754d522f475Smrg    int maxcol = screen->max_col;
2755d522f475Smrg
2756d522f475Smrg#define scroll_update_one(cell) \
2757d522f475Smrg	(cell)->row += amount; \
2758d522f475Smrg	if ((cell)->row < minrow) { \
2759d522f475Smrg	    (cell)->row = minrow; \
2760d522f475Smrg	    (cell)->col = 0; \
2761d522f475Smrg	} \
2762d522f475Smrg	if ((cell)->row > maxrow) { \
2763d522f475Smrg	    (cell)->row = maxrow; \
2764d522f475Smrg	    (cell)->col = maxcol; \
2765d522f475Smrg	}
2766d522f475Smrg
2767d522f475Smrg    scroll_update_one(&(screen->startRaw));
2768d522f475Smrg    scroll_update_one(&(screen->endRaw));
2769d522f475Smrg    scroll_update_one(&(screen->startSel));
2770d522f475Smrg    scroll_update_one(&(screen->endSel));
2771d522f475Smrg
2772d522f475Smrg    scroll_update_one(&(screen->rawPos));
2773d522f475Smrg
2774d522f475Smrg    /*
2775d522f475Smrg     * If we are told to scroll the selection but it lies outside the scrolling
2776d522f475Smrg     * margins, then that could cause the selection to move (bad).  It is not
2777d522f475Smrg     * simple to fix, because this function is called both for the scrollbar
2778d522f475Smrg     * actions as well as application scrolling.  The 'always' flag is set in
2779d522f475Smrg     * the former case.  The rest of the logic handles the latter.
2780d522f475Smrg     */
2781d522f475Smrg    if (ScrnHaveSelection(screen)) {
2782d522f475Smrg	int adjust;
2783d522f475Smrg
2784d522f475Smrg	adjust = ROW2INX(screen, screen->startH.row);
2785d522f475Smrg	if (always
27860bd37d32Smrg	    || !ScrnHaveRowMargins(screen)
27870bd37d32Smrg	    || ScrnIsRowInMargins(screen, adjust)) {
2788d522f475Smrg	    scroll_update_one(&screen->startH);
2789d522f475Smrg	}
2790d522f475Smrg	adjust = ROW2INX(screen, screen->endH.row);
2791d522f475Smrg	if (always
27920bd37d32Smrg	    || !ScrnHaveRowMargins(screen)
27930bd37d32Smrg	    || ScrnIsRowInMargins(screen, adjust)) {
2794d522f475Smrg	    scroll_update_one(&screen->endH);
2795d522f475Smrg	}
2796d522f475Smrg    }
2797d522f475Smrg
2798d522f475Smrg    screen->startHCoord = Coordinate(screen, &screen->startH);
2799d522f475Smrg    screen->endHCoord = Coordinate(screen, &screen->endH);
2800d522f475Smrg}
2801d522f475Smrg
2802d522f475Smrg/*ARGSUSED*/
2803d522f475Smrgvoid
2804e0a2b6dfSmrgResizeSelection(TScreen *screen GCC_UNUSED, int rows, int cols)
2805d522f475Smrg{
2806d522f475Smrg    rows--;			/* decr to get 0-max */
2807d522f475Smrg    cols--;
2808d522f475Smrg
2809d522f475Smrg    if (screen->startRaw.row > rows)
2810d522f475Smrg	screen->startRaw.row = rows;
2811d522f475Smrg    if (screen->startSel.row > rows)
2812d522f475Smrg	screen->startSel.row = rows;
2813d522f475Smrg    if (screen->endRaw.row > rows)
2814d522f475Smrg	screen->endRaw.row = rows;
2815d522f475Smrg    if (screen->endSel.row > rows)
2816d522f475Smrg	screen->endSel.row = rows;
2817d522f475Smrg    if (screen->rawPos.row > rows)
2818d522f475Smrg	screen->rawPos.row = rows;
2819d522f475Smrg
2820d522f475Smrg    if (screen->startRaw.col > cols)
2821d522f475Smrg	screen->startRaw.col = cols;
2822d522f475Smrg    if (screen->startSel.col > cols)
2823d522f475Smrg	screen->startSel.col = cols;
2824d522f475Smrg    if (screen->endRaw.col > cols)
2825d522f475Smrg	screen->endRaw.col = cols;
2826d522f475Smrg    if (screen->endSel.col > cols)
2827d522f475Smrg	screen->endSel.col = cols;
2828d522f475Smrg    if (screen->rawPos.col > cols)
2829d522f475Smrg	screen->rawPos.col = cols;
2830d522f475Smrg}
2831d522f475Smrg
2832d522f475Smrg#if OPT_WIDE_CHARS
2833d522f475SmrgBool
2834d522f475Smrgiswide(int i)
2835d522f475Smrg{
283620d2c4d2Smrg    return (i == HIDDEN_CHAR) || (WideCells(i) == 2);
2837d522f475Smrg}
2838d522f475Smrg
2839d522f475Smrg#define isWideCell(row, col) iswide((int)XTERM_CELL(row, col))
2840d522f475Smrg#endif
2841d522f475Smrg
2842d522f475Smrgstatic void
2843e0a2b6dfSmrgPointToCELL(TScreen *screen,
2844d522f475Smrg	    int y,
2845d522f475Smrg	    int x,
2846e0a2b6dfSmrg	    CELL *cell)
2847d522f475Smrg/* Convert pixel coordinates to character coordinates.
2848d522f475Smrg   Rows are clipped between firstValidRow and lastValidRow.
2849d522f475Smrg   Columns are clipped between to be 0 or greater, but are not clipped to some
2850d522f475Smrg       maximum value. */
2851d522f475Smrg{
2852d522f475Smrg    cell->row = (y - screen->border) / FontHeight(screen);
2853d522f475Smrg    if (cell->row < screen->firstValidRow)
2854d522f475Smrg	cell->row = screen->firstValidRow;
2855d522f475Smrg    else if (cell->row > screen->lastValidRow)
2856d522f475Smrg	cell->row = screen->lastValidRow;
2857d522f475Smrg    cell->col = (x - OriginX(screen)) / FontWidth(screen);
2858d522f475Smrg    if (cell->col < 0)
2859d522f475Smrg	cell->col = 0;
2860d522f475Smrg    else if (cell->col > MaxCols(screen)) {
2861d522f475Smrg	cell->col = MaxCols(screen);
2862d522f475Smrg    }
2863d522f475Smrg#if OPT_WIDE_CHARS
2864d522f475Smrg    /*
2865d522f475Smrg     * If we got a click on the right half of a doublewidth character,
2866d522f475Smrg     * pretend it happened on the left half.
2867d522f475Smrg     */
2868d522f475Smrg    if (cell->col > 0
2869d522f475Smrg	&& isWideCell(cell->row, cell->col - 1)
2870d522f475Smrg	&& (XTERM_CELL(cell->row, cell->col) == HIDDEN_CHAR)) {
2871d522f475Smrg	cell->col -= 1;
2872d522f475Smrg    }
2873d522f475Smrg#endif
2874d522f475Smrg}
2875d522f475Smrg
2876d522f475Smrg/*
2877d522f475Smrg * Find the last column at which text was drawn on the given row.
2878d522f475Smrg */
2879d522f475Smrgstatic int
288001037d57SmrgLastTextCol(TScreen *screen, CLineData *ld, int row)
2881d522f475Smrg{
288220d2c4d2Smrg    int i = -1;
2883d522f475Smrg
288420d2c4d2Smrg    if (ld != 0) {
288520d2c4d2Smrg	if (okScrnRow(screen, row)) {
28862e4f8982Smrg	    const IAttr *ch;
288720d2c4d2Smrg	    for (i = screen->max_col,
288820d2c4d2Smrg		 ch = ld->attribs + i;
288920d2c4d2Smrg		 i >= 0 && !(*ch & CHARDRAWN);
289020d2c4d2Smrg		 ch--, i--) {
289120d2c4d2Smrg		;
289220d2c4d2Smrg	    }
2893d522f475Smrg#if OPT_DEC_CHRSET
289420d2c4d2Smrg	    if (CSET_DOUBLE(GetLineDblCS(ld))) {
289520d2c4d2Smrg		i *= 2;
289620d2c4d2Smrg	    }
2897d522f475Smrg#endif
289820d2c4d2Smrg	}
2899d522f475Smrg    }
2900d522f475Smrg    return (i);
2901d522f475Smrg}
2902d522f475Smrg
2903d522f475Smrg#if !OPT_WIDE_CHARS
2904d522f475Smrg/*
2905d522f475Smrg** double click table for cut and paste in 8 bits
2906d522f475Smrg**
2907d522f475Smrg** This table is divided in four parts :
2908d522f475Smrg**
2909d522f475Smrg**	- control characters	[0,0x1f] U [0x80,0x9f]
2910d522f475Smrg**	- separators		[0x20,0x3f] U [0xa0,0xb9]
2911d522f475Smrg**	- binding characters	[0x40,0x7f] U [0xc0,0xff]
2912d522f475Smrg**	- exceptions
2913d522f475Smrg*/
2914d522f475Smrg/* *INDENT-OFF* */
2915d522f475Smrgstatic int charClass[256] =
2916d522f475Smrg{
2917d522f475Smrg/* NUL  SOH  STX  ETX  EOT  ENQ  ACK  BEL */
2918d522f475Smrg    32,  1,    1,   1,   1,   1,   1,   1,
29190bd37d32Smrg/*  BS   HT   NL   VT   FF   CR   SO   SI */
2920d522f475Smrg     1,  32,   1,   1,   1,   1,   1,   1,
2921d522f475Smrg/* DLE  DC1  DC2  DC3  DC4  NAK  SYN  ETB */
2922d522f475Smrg     1,   1,   1,   1,   1,   1,   1,   1,
2923d522f475Smrg/* CAN   EM  SUB  ESC   FS   GS   RS   US */
2924d522f475Smrg     1,   1,   1,   1,   1,   1,   1,   1,
2925d522f475Smrg/*  SP    !    "    #    $    %    &    ' */
2926d522f475Smrg    32,  33,  34,  35,  36,  37,  38,  39,
2927d522f475Smrg/*   (    )    *    +    ,    -    .    / */
2928d522f475Smrg    40,  41,  42,  43,  44,  45,  46,  47,
2929d522f475Smrg/*   0    1    2    3    4    5    6    7 */
2930d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
2931d522f475Smrg/*   8    9    :    ;    <    =    >    ? */
2932d522f475Smrg    48,  48,  58,  59,  60,  61,  62,  63,
2933d522f475Smrg/*   @    A    B    C    D    E    F    G */
2934d522f475Smrg    64,  48,  48,  48,  48,  48,  48,  48,
2935d522f475Smrg/*   H    I    J    K    L    M    N    O */
2936d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
2937d522f475Smrg/*   P    Q    R    S    T    U    V    W */
2938d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
2939d522f475Smrg/*   X    Y    Z    [    \    ]    ^    _ */
2940d522f475Smrg    48,  48,  48,  91,  92,  93,  94,  48,
2941d522f475Smrg/*   `    a    b    c    d    e    f    g */
2942d522f475Smrg    96,  48,  48,  48,  48,  48,  48,  48,
2943d522f475Smrg/*   h    i    j    k    l    m    n    o */
2944d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
2945d522f475Smrg/*   p    q    r    s    t    u    v    w */
2946d522f475Smrg    48,  48,  48,  48,  48,  48,  48,  48,
2947d522f475Smrg/*   x    y    z    {    |    }    ~  DEL */
2948d522f475Smrg    48,  48,  48, 123, 124, 125, 126,   1,
2949d522f475Smrg/* x80  x81  x82  x83  IND  NEL  SSA  ESA */
2950d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
2951d522f475Smrg/* HTS  HTJ  VTS  PLD  PLU   RI  SS2  SS3 */
2952d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
2953d522f475Smrg/* DCS  PU1  PU2  STS  CCH   MW  SPA  EPA */
2954d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
2955d522f475Smrg/* x98  x99  x9A  CSI   ST  OSC   PM  APC */
2956d522f475Smrg    1,    1,   1,   1,   1,   1,   1,   1,
2957d522f475Smrg/*   -    i   c/    L   ox   Y-    |   So */
2958d522f475Smrg    160, 161, 162, 163, 164, 165, 166, 167,
2959d522f475Smrg/*  ..   c0   ip   <<    _        R0    - */
2960d522f475Smrg    168, 169, 170, 171, 172, 173, 174, 175,
2961d522f475Smrg/*   o   +-    2    3    '    u   q|    . */
2962d522f475Smrg    176, 177, 178, 179, 180, 181, 182, 183,
2963d522f475Smrg/*   ,    1    2   >>  1/4  1/2  3/4    ? */
2964d522f475Smrg    184, 185, 186, 187, 188, 189, 190, 191,
2965d522f475Smrg/*  A`   A'   A^   A~   A:   Ao   AE   C, */
2966d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
2967d522f475Smrg/*  E`   E'   E^   E:   I`   I'   I^   I: */
2968d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
2969d522f475Smrg/*  D-   N~   O`   O'   O^   O~   O:    X */
2970d522f475Smrg     48,  48,  48,  48,  48,  48,  48, 215,
2971d522f475Smrg/*  O/   U`   U'   U^   U:   Y'    P    B */
2972d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
2973d522f475Smrg/*  a`   a'   a^   a~   a:   ao   ae   c, */
2974d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
2975d522f475Smrg/*  e`   e'   e^   e:    i`  i'   i^   i: */
2976d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48,
2977d522f475Smrg/*   d   n~   o`   o'   o^   o~   o:   -: */
2978d522f475Smrg     48,  48,  48,  48,  48,  48,  48, 247,
2979d522f475Smrg/*  o/   u`   u'   u^   u:   y'    P   y: */
2980d522f475Smrg     48,  48,  48,  48,  48,  48,  48,  48};
2981d522f475Smrg/* *INDENT-ON* */
2982d522f475Smrg
2983d522f475Smrgint
2984d522f475SmrgSetCharacterClassRange(int low,	/* in range of [0..255] */
2985d522f475Smrg		       int high,
2986d522f475Smrg		       int value)	/* arbitrary */
2987d522f475Smrg{
2988d522f475Smrg
2989d522f475Smrg    if (low < 0 || high > 255 || high < low)
2990d522f475Smrg	return (-1);
2991d522f475Smrg
2992d522f475Smrg    for (; low <= high; low++)
2993d522f475Smrg	charClass[low] = value;
2994d522f475Smrg
2995d522f475Smrg    return (0);
2996d522f475Smrg}
2997d522f475Smrg#endif
2998d522f475Smrg
2999d522f475Smrgstatic int
3000e0a2b6dfSmrgclass_of(LineData *ld, CELL *cell)
3001d522f475Smrg{
3002d522f475Smrg    CELL temp = *cell;
30030bd37d32Smrg    int result = 0;
3004d522f475Smrg
3005d522f475Smrg#if OPT_DEC_CHRSET
3006956cc18dSsnj    if (CSET_DOUBLE(GetLineDblCS(ld))) {
3007d522f475Smrg	temp.col /= 2;
3008d522f475Smrg    }
3009d522f475Smrg#endif
30100bd37d32Smrg    if (temp.col < (int) ld->lineSize)
30110bd37d32Smrg	result = CharacterClass((int) (ld->charData[temp.col]));
30120bd37d32Smrg    return result;
3013d522f475Smrg}
3014956cc18dSsnj
3015956cc18dSsnj#if OPT_WIDE_CHARS
3016956cc18dSsnj#define CClassSelects(name, cclass) \
3017956cc18dSsnj	 (CClassOf(name) == cclass \
3018956cc18dSsnj	 || XTERM_CELL(screen->name.row, screen->name.col) == HIDDEN_CHAR)
3019d522f475Smrg#else
3020956cc18dSsnj#define CClassSelects(name, cclass) \
3021956cc18dSsnj	 (class_of(ld.name, &((screen->name))) == cclass)
3022d522f475Smrg#endif
3023d522f475Smrg
3024956cc18dSsnj#define CClassOf(name) class_of(ld.name, &((screen->name)))
3025956cc18dSsnj
3026913cc679Smrg#if OPT_REPORT_CCLASS
3027913cc679Smrgstatic int
3028913cc679Smrgshow_cclass_range(int lo, int hi)
3029913cc679Smrg{
3030913cc679Smrg    int cclass = CharacterClass(lo);
3031913cc679Smrg    int ident = (cclass == lo);
3032913cc679Smrg    int more = 0;
3033913cc679Smrg    if (ident) {
3034913cc679Smrg	int ch;
3035913cc679Smrg	for (ch = lo + 1; ch <= hi; ch++) {
3036913cc679Smrg	    if (CharacterClass(ch) != ch) {
3037913cc679Smrg		ident = 0;
3038913cc679Smrg		break;
3039913cc679Smrg	    }
3040913cc679Smrg	}
3041913cc679Smrg	if (ident && (hi < 255)) {
3042913cc679Smrg	    ch = hi + 1;
3043913cc679Smrg	    if (CharacterClass(ch) == ch) {
3044913cc679Smrg		if (ch >= 255 || CharacterClass(ch + 1) != ch) {
3045913cc679Smrg		    more = 1;
3046913cc679Smrg		}
3047913cc679Smrg	    }
3048913cc679Smrg	}
3049913cc679Smrg    }
3050913cc679Smrg    if (!more) {
3051913cc679Smrg	if (lo == hi) {
3052913cc679Smrg	    printf("\t%d", lo);
3053913cc679Smrg	} else {
3054913cc679Smrg	    printf("\t%d-%d", lo, hi);
3055913cc679Smrg	}
3056913cc679Smrg	if (!ident)
3057913cc679Smrg	    printf(":%d", cclass);
3058913cc679Smrg	if (hi < 255)
3059913cc679Smrg	    printf(", \\");
3060913cc679Smrg	printf("\n");
3061913cc679Smrg    }
3062913cc679Smrg    return !more;
3063913cc679Smrg}
3064913cc679Smrg
3065913cc679Smrgvoid
3066913cc679Smrgreport_char_class(XtermWidget xw)
3067913cc679Smrg{
3068913cc679Smrg    /* simple table, to match documentation */
3069913cc679Smrg    static const char charnames[] =
3070913cc679Smrg    "NUL\0" "SOH\0" "STX\0" "ETX\0" "EOT\0" "ENQ\0" "ACK\0" "BEL\0"
3071913cc679Smrg    " BS\0" " HT\0" " NL\0" " VT\0" " NP\0" " CR\0" " SO\0" " SI\0"
3072913cc679Smrg    "DLE\0" "DC1\0" "DC2\0" "DC3\0" "DC4\0" "NAK\0" "SYN\0" "ETB\0"
3073913cc679Smrg    "CAN\0" " EM\0" "SUB\0" "ESC\0" " FS\0" " GS\0" " RS\0" " US\0"
3074913cc679Smrg    " SP\0" "  !\0" "  \"\0" "  #\0" "  $\0" "  %\0" "  &\0" "  '\0"
3075913cc679Smrg    "  (\0" "  )\0" "  *\0" "  +\0" "  ,\0" "  -\0" "  .\0" "  /\0"
3076913cc679Smrg    "  0\0" "  1\0" "  2\0" "  3\0" "  4\0" "  5\0" "  6\0" "  7\0"
3077913cc679Smrg    "  8\0" "  9\0" "  :\0" "  ;\0" "  <\0" "  =\0" "  >\0" "  ?\0"
3078913cc679Smrg    "  @\0" "  A\0" "  B\0" "  C\0" "  D\0" "  E\0" "  F\0" "  G\0"
3079913cc679Smrg    "  H\0" "  I\0" "  J\0" "  K\0" "  L\0" "  M\0" "  N\0" "  O\0"
3080913cc679Smrg    "  P\0" "  Q\0" "  R\0" "  S\0" "  T\0" "  U\0" "  V\0" "  W\0"
3081913cc679Smrg    "  X\0" "  Y\0" "  Z\0" "  [\0" "  \\\0" "  ]\0" "  ^\0" "  _\0"
3082913cc679Smrg    "  `\0" "  a\0" "  b\0" "  c\0" "  d\0" "  e\0" "  f\0" "  g\0"
3083913cc679Smrg    "  h\0" "  i\0" "  j\0" "  k\0" "  l\0" "  m\0" "  n\0" "  o\0"
3084913cc679Smrg    "  p\0" "  q\0" "  r\0" "  s\0" "  t\0" "  u\0" "  v\0" "  w\0"
3085913cc679Smrg    "  x\0" "  y\0" "  z\0" "  {\0" "  |\0" "  }\0" "  ~\0" "DEL\0"
3086913cc679Smrg    "x80\0" "x81\0" "x82\0" "x83\0" "IND\0" "NEL\0" "SSA\0" "ESA\0"
3087913cc679Smrg    "HTS\0" "HTJ\0" "VTS\0" "PLD\0" "PLU\0" " RI\0" "SS2\0" "SS3\0"
3088913cc679Smrg    "DCS\0" "PU1\0" "PU2\0" "STS\0" "CCH\0" " MW\0" "SPA\0" "EPA\0"
3089913cc679Smrg    "x98\0" "x99\0" "x9A\0" "CSI\0" " ST\0" "OSC\0" " PM\0" "APC\0"
3090913cc679Smrg    "  -\0" "  i\0" " c/\0" "  L\0" " ox\0" " Y-\0" "  |\0" " So\0"
3091913cc679Smrg    " ..\0" " c0\0" " ip\0" " <<\0" "  _\0" "   \0" " R0\0" "  -\0"
3092913cc679Smrg    "  o\0" " +-\0" "  2\0" "  3\0" "  '\0" "  u\0" " q|\0" "  .\0"
3093913cc679Smrg    "  ,\0" "  1\0" "  2\0" " >>\0" "1/4\0" "1/2\0" "3/4\0" "  ?\0"
3094913cc679Smrg    " A`\0" " A'\0" " A^\0" " A~\0" " A:\0" " Ao\0" " AE\0" " C,\0"
3095913cc679Smrg    " E`\0" " E'\0" " E^\0" " E:\0" " I`\0" " I'\0" " I^\0" " I:\0"
3096913cc679Smrg    " D-\0" " N~\0" " O`\0" " O'\0" " O^\0" " O~\0" " O:\0" "  X\0"
3097913cc679Smrg    " O/\0" " U`\0" " U'\0" " U^\0" " U:\0" " Y'\0" "  P\0" "  B\0"
3098913cc679Smrg    " a`\0" " a'\0" " a^\0" " a~\0" " a:\0" " ao\0" " ae\0" " c,\0"
3099913cc679Smrg    " e`\0" " e'\0" " e^\0" " e:\0" " i`\0" " i'\0" " i^\0" " i:\0"
3100913cc679Smrg    "  d\0" " n~\0" " o`\0" " o'\0" " o^\0" " o~\0" " o:\0" " -:\0"
3101913cc679Smrg    " o/\0" " u`\0" " u'\0" " u^\0" " u:\0" " y'\0" "  P\0" " y:\0";
3102913cc679Smrg    int ch, dh;
3103913cc679Smrg    int class_p;
3104913cc679Smrg
3105913cc679Smrg    (void) xw;
3106913cc679Smrg
3107913cc679Smrg    printf("static int charClass[256] = {\n");
3108913cc679Smrg    for (ch = 0; ch < 256; ++ch) {
3109913cc679Smrg	const char *s = charnames + (ch * 4);
3110913cc679Smrg	if ((ch & 7) == 0)
3111913cc679Smrg	    printf("/*");
3112913cc679Smrg	printf(" %s ", s);
3113913cc679Smrg	if (((ch + 1) & 7) == 0) {
3114913cc679Smrg	    printf("*/\n  ");
3115913cc679Smrg	    for (dh = ch - 7; dh <= ch; ++dh) {
3116913cc679Smrg		printf(" %3d%s", CharacterClass(dh), dh == 255 ? "};" : ",");
3117913cc679Smrg	    }
3118913cc679Smrg	    printf("\n");
3119913cc679Smrg	}
3120913cc679Smrg    }
3121913cc679Smrg
3122913cc679Smrg    /* print the table as if it were the charClass resource */
3123913cc679Smrg    printf("\n");
3124913cc679Smrg    printf("The table is equivalent to this \"charClass\" resource:\n");
3125913cc679Smrg    class_p = CharacterClass(dh = 0);
3126913cc679Smrg    for (ch = 0; ch < 256; ++ch) {
3127913cc679Smrg	int class_c = CharacterClass(ch);
3128913cc679Smrg	if (class_c != class_p) {
3129913cc679Smrg	    if (show_cclass_range(dh, ch - 1)) {
3130913cc679Smrg		dh = ch;
3131913cc679Smrg		class_p = class_c;
3132913cc679Smrg	    }
3133913cc679Smrg	}
3134913cc679Smrg    }
3135913cc679Smrg    if (dh < 255) {
3136913cc679Smrg	show_cclass_range(dh, 255);
3137913cc679Smrg    }
3138913cc679Smrg
3139913cc679Smrg    if_OPT_WIDE_CHARS(TScreenOf(xw), {
3140913cc679Smrg	/* if this is a wide-character configuration, print all intervals */
3141913cc679Smrg	report_wide_char_class();
3142913cc679Smrg    });
3143913cc679Smrg}
3144913cc679Smrg#endif
3145913cc679Smrg
3146d522f475Smrg/*
3147d522f475Smrg * If the given column is past the end of text on the given row, bump to the
3148d522f475Smrg * beginning of the next line.
3149d522f475Smrg */
3150d522f475Smrgstatic Boolean
3151e0a2b6dfSmrgokPosition(TScreen *screen,
3152e0a2b6dfSmrg	   LineData **ld,
3153e0a2b6dfSmrg	   CELL *cell)
3154d522f475Smrg{
315520d2c4d2Smrg    Boolean result = True;
315620d2c4d2Smrg
315720d2c4d2Smrg    if (cell->row > screen->max_row) {
315820d2c4d2Smrg	result = False;
315920d2c4d2Smrg    } else if (cell->col > (LastTextCol(screen, *ld, cell->row) + 1)) {
316020d2c4d2Smrg	if (cell->row < screen->max_row) {
316120d2c4d2Smrg	    cell->col = 0;
316220d2c4d2Smrg	    *ld = GET_LINEDATA(screen, ++cell->row);
316320d2c4d2Smrg	    result = False;
316420d2c4d2Smrg	}
3165d522f475Smrg    }
316620d2c4d2Smrg    return result;
3167d522f475Smrg}
3168d522f475Smrg
3169d522f475Smrgstatic void
3170e0a2b6dfSmrgtrimLastLine(TScreen *screen,
3171e0a2b6dfSmrg	     LineData **ld,
3172e0a2b6dfSmrg	     CELL *last)
3173d522f475Smrg{
317420d2c4d2Smrg    if (screen->cutNewline && last->row < screen->max_row) {
3175d522f475Smrg	last->col = 0;
3176956cc18dSsnj	*ld = GET_LINEDATA(screen, ++last->row);
3177d522f475Smrg    } else {
3178956cc18dSsnj	last->col = LastTextCol(screen, *ld, last->row) + 1;
3179d522f475Smrg    }
3180d522f475Smrg}
3181d522f475Smrg
3182d522f475Smrg#if OPT_SELECT_REGEX
3183d522f475Smrg/*
3184d522f475Smrg * Returns the first row of a wrapped line.
3185d522f475Smrg */
3186d522f475Smrgstatic int
3187e0a2b6dfSmrgfirstRowOfLine(TScreen *screen, int row, Bool visible)
3188d522f475Smrg{
3189956cc18dSsnj    LineData *ld = 0;
3190d522f475Smrg    int limit = visible ? 0 : -screen->savedlines;
3191d522f475Smrg
3192d522f475Smrg    while (row > limit &&
3193956cc18dSsnj	   (ld = GET_LINEDATA(screen, row - 1)) != 0 &&
3194956cc18dSsnj	   LineTstWrapped(ld)) {
3195d522f475Smrg	--row;
3196956cc18dSsnj    }
3197d522f475Smrg    return row;
3198d522f475Smrg}
3199d522f475Smrg
3200d522f475Smrg/*
3201d522f475Smrg * Returns the last row of a wrapped line.
3202d522f475Smrg */
3203d522f475Smrgstatic int
3204e0a2b6dfSmrglastRowOfLine(TScreen *screen, int row)
3205d522f475Smrg{
3206956cc18dSsnj    LineData *ld;
3207956cc18dSsnj
3208d522f475Smrg    while (row < screen->max_row &&
3209956cc18dSsnj	   (ld = GET_LINEDATA(screen, row)) != 0 &&
3210956cc18dSsnj	   LineTstWrapped(ld)) {
3211d522f475Smrg	++row;
3212956cc18dSsnj    }
3213d522f475Smrg    return row;
3214d522f475Smrg}
3215d522f475Smrg
3216d522f475Smrg/*
3217d522f475Smrg * Returns the number of cells on the range of rows.
3218d522f475Smrg */
3219d522f475Smrgstatic unsigned
3220e0a2b6dfSmrglengthOfLines(TScreen *screen, int firstRow, int lastRow)
3221d522f475Smrg{
3222d522f475Smrg    unsigned length = 0;
3223d522f475Smrg    int n;
3224d522f475Smrg
3225d522f475Smrg    for (n = firstRow; n <= lastRow; ++n) {
3226956cc18dSsnj	LineData *ld = GET_LINEDATA(screen, n);
3227956cc18dSsnj	int value = LastTextCol(screen, ld, n);
3228d522f475Smrg	if (value >= 0)
32292eaa94a1Schristos	    length += (unsigned) (value + 1);
3230d522f475Smrg    }
3231d522f475Smrg    return length;
3232d522f475Smrg}
3233d522f475Smrg
3234d522f475Smrg/*
3235d522f475Smrg * Make a copy of the wrapped-line which corresponds to the given row as a
3236d522f475Smrg * string of bytes.  Construct an index for the columns from the beginning of
3237d522f475Smrg * the line.
3238d522f475Smrg */
3239d522f475Smrgstatic char *
3240e0a2b6dfSmrgmake_indexed_text(TScreen *screen, int row, unsigned length, int *indexed)
3241d522f475Smrg{
3242d522f475Smrg    Char *result = 0;
324320d2c4d2Smrg    size_t need = (length + 1);
3244d522f475Smrg
3245d522f475Smrg    /*
3246d522f475Smrg     * Get a quick upper bound to the number of bytes needed, if the whole
3247d522f475Smrg     * string were UTF-8.
3248d522f475Smrg     */
3249d522f475Smrg    if_OPT_WIDE_CHARS(screen, {
3250956cc18dSsnj	need *= ((screen->lineExtra + 1) * 6);
3251d522f475Smrg    });
3252d522f475Smrg
3253d522f475Smrg    if ((result = TypeCallocN(Char, need + 1)) != 0) {
3254956cc18dSsnj	LineData *ld = GET_LINEDATA(screen, row);
3255d522f475Smrg	unsigned used = 0;
3256d522f475Smrg	Char *last = result;
3257d522f475Smrg
3258d522f475Smrg	do {
3259d522f475Smrg	    int col = 0;
3260956cc18dSsnj	    int limit = LastTextCol(screen, ld, row);
3261d522f475Smrg
3262d522f475Smrg	    while (col <= limit) {
3263d522f475Smrg		Char *next = last;
3264956cc18dSsnj		unsigned data = ld->charData[col];
3265d522f475Smrg
32660bd37d32Smrg		assert(col < (int) ld->lineSize);
3267d522f475Smrg		/* some internal points may not be drawn */
3268d522f475Smrg		if (data == 0)
3269d522f475Smrg		    data = ' ';
3270d522f475Smrg
3271d522f475Smrg		if_WIDE_OR_NARROW(screen, {
3272d522f475Smrg		    next = convertToUTF8(last, data);
3273d522f475Smrg		}
3274d522f475Smrg		, {
3275d522f475Smrg		    *next++ = CharOf(data);
3276d522f475Smrg		});
3277d522f475Smrg
3278d522f475Smrg		if_OPT_WIDE_CHARS(screen, {
3279956cc18dSsnj		    size_t off;
3280956cc18dSsnj		    for_each_combData(off, ld) {
3281956cc18dSsnj			data = ld->combData[off][col];
3282956cc18dSsnj			if (data == 0)
3283d522f475Smrg			    break;
3284d522f475Smrg			next = convertToUTF8(next, data);
3285d522f475Smrg		    }
3286d522f475Smrg		});
3287d522f475Smrg
328820d2c4d2Smrg		indexed[used] = (int) (last - result);
3289d522f475Smrg		*next = 0;
3290d522f475Smrg		/* TRACE(("index[%d.%d] %d:%s\n", row, used, indexed[used], last)); */
3291d522f475Smrg		last = next;
3292d522f475Smrg		++used;
3293d522f475Smrg		++col;
329420d2c4d2Smrg		indexed[used] = (int) (next - result);
3295d522f475Smrg	    }
3296d522f475Smrg	} while (used < length &&
3297956cc18dSsnj		 LineTstWrapped(ld) &&
3298956cc18dSsnj		 (ld = GET_LINEDATA(screen, ++row)) != 0 &&
3299956cc18dSsnj		 row < screen->max_row);
3300d522f475Smrg    }
3301d522f475Smrg    /* TRACE(("result:%s\n", result)); */
3302d522f475Smrg    return (char *) result;
3303d522f475Smrg}
3304d522f475Smrg
3305d522f475Smrg/*
3306d522f475Smrg * Find the column given an offset into the character string by using the
3307d522f475Smrg * index constructed in make_indexed_text().
3308d522f475Smrg */
3309d522f475Smrgstatic int
3310d522f475SmrgindexToCol(int *indexed, int len, int off)
3311d522f475Smrg{
3312d522f475Smrg    int col = 0;
3313d522f475Smrg    while (indexed[col] < len) {
3314d522f475Smrg	if (indexed[col] >= off)
3315d522f475Smrg	    break;
3316d522f475Smrg	++col;
3317d522f475Smrg    }
3318d522f475Smrg    return col;
3319d522f475Smrg}
3320d522f475Smrg
3321d522f475Smrg/*
3322d522f475Smrg * Given a row number, and a column offset from that (which may be wrapped),
3323d522f475Smrg * set the cell to the actual row/column values.
3324d522f475Smrg */
3325d522f475Smrgstatic void
3326e0a2b6dfSmrgcolumnToCell(TScreen *screen, int row, int col, CELL *cell)
3327d522f475Smrg{
3328d522f475Smrg    while (row < screen->max_row) {
332901037d57Smrg	CLineData *ld = GET_LINEDATA(screen, row);
3330956cc18dSsnj	int last = LastTextCol(screen, ld, row);
3331d522f475Smrg
3332d522f475Smrg	/* TRACE(("last(%d) = %d, have %d\n", row, last, col)); */
3333d522f475Smrg	if (col <= last) {
3334d522f475Smrg	    break;
3335d522f475Smrg	}
3336d522f475Smrg	/*
3337d522f475Smrg	 * Stop if the current row does not wrap (does not continue the current
3338d522f475Smrg	 * line).
3339d522f475Smrg	 */
3340956cc18dSsnj	if (!LineTstWrapped(ld)) {
3341d522f475Smrg	    col = last + 1;
3342d522f475Smrg	    break;
3343d522f475Smrg	}
3344d522f475Smrg	col -= (last + 1);
3345d522f475Smrg	++row;
3346d522f475Smrg    }
3347d522f475Smrg    if (col < 0)
3348d522f475Smrg	col = 0;
3349d522f475Smrg    cell->row = row;
3350d522f475Smrg    cell->col = col;
3351d522f475Smrg}
3352d522f475Smrg
3353d522f475Smrg/*
3354d522f475Smrg * Given a cell, find the corresponding column offset.
3355d522f475Smrg */
3356d522f475Smrgstatic int
3357e0a2b6dfSmrgcellToColumn(TScreen *screen, CELL *cell)
3358d522f475Smrg{
335901037d57Smrg    CLineData *ld = 0;
3360d522f475Smrg    int col = cell->col;
3361d522f475Smrg    int row = firstRowOfLine(screen, cell->row, False);
3362d522f475Smrg    while (row < cell->row) {
3363956cc18dSsnj	ld = GET_LINEDATA(screen, row);
3364956cc18dSsnj	col += LastTextCol(screen, ld, row++);
3365d522f475Smrg    }
3366956cc18dSsnj#if OPT_DEC_CHRSET
3367956cc18dSsnj    if (ld == 0)
3368956cc18dSsnj	ld = GET_LINEDATA(screen, row);
3369956cc18dSsnj    if (CSET_DOUBLE(GetLineDblCS(ld)))
3370956cc18dSsnj	col /= 2;
3371956cc18dSsnj#endif
3372d522f475Smrg    return col;
3373d522f475Smrg}
3374d522f475Smrg
3375d522f475Smrgstatic void
3376e0a2b6dfSmrgdo_select_regex(TScreen *screen, CELL *startc, CELL *endc)
3377d522f475Smrg{
3378956cc18dSsnj    LineData *ld = GET_LINEDATA(screen, startc->row);
3379d522f475Smrg    int inx = ((screen->numberOfClicks - 1) % screen->maxClicks);
3380d522f475Smrg    char *expr = screen->selectExpr[inx];
3381d522f475Smrg    regex_t preg;
3382d522f475Smrg    regmatch_t match;
3383d522f475Smrg
338401037d57Smrg    TRACE(("Select_REGEX[%d]:%s\n", inx, NonNull(expr)));
3385956cc18dSsnj    if (okPosition(screen, &ld, startc) && expr != 0) {
3386d522f475Smrg	if (regcomp(&preg, expr, REG_EXTENDED) == 0) {
3387d522f475Smrg	    int firstRow = firstRowOfLine(screen, startc->row, True);
3388d522f475Smrg	    int lastRow = lastRowOfLine(screen, firstRow);
3389d522f475Smrg	    unsigned size = lengthOfLines(screen, firstRow, lastRow);
3390d522f475Smrg	    int actual = cellToColumn(screen, startc);
33912e4f8982Smrg	    int *indexed;
3392d522f475Smrg
3393d522f475Smrg	    TRACE(("regcomp ok rows %d..%d bytes %d\n",
3394d522f475Smrg		   firstRow, lastRow, size));
3395d522f475Smrg
3396d522f475Smrg	    if ((indexed = TypeCallocN(int, size + 1)) != 0) {
33972e4f8982Smrg		char *search;
3398d522f475Smrg		if ((search = make_indexed_text(screen,
3399d522f475Smrg						firstRow,
3400d522f475Smrg						size,
3401d522f475Smrg						indexed)) != 0) {
34022eaa94a1Schristos		    int len = (int) strlen(search);
3403d522f475Smrg		    int col;
3404d522f475Smrg		    int best_col = -1;
3405d522f475Smrg		    int best_len = -1;
3406d522f475Smrg
3407913cc679Smrg		    startc->row = 0;
3408913cc679Smrg		    startc->col = 0;
3409913cc679Smrg		    endc->row = 0;
3410913cc679Smrg		    endc->col = 0;
3411913cc679Smrg
3412d522f475Smrg		    for (col = 0; indexed[col] < len; ++col) {
3413d522f475Smrg			if (regexec(&preg,
3414d522f475Smrg				    search + indexed[col],
341520d2c4d2Smrg				    (size_t) 1, &match, 0) == 0) {
3416894e0ac8Smrg			    int start_inx = (int) (match.rm_so + indexed[col]);
3417894e0ac8Smrg			    int finis_inx = (int) (match.rm_eo + indexed[col]);
3418d522f475Smrg			    int start_col = indexToCol(indexed, len, start_inx);
3419d522f475Smrg			    int finis_col = indexToCol(indexed, len, finis_inx);
3420d522f475Smrg
3421d522f475Smrg			    if (start_col <= actual &&
3422913cc679Smrg				actual <= finis_col) {
3423d522f475Smrg				int test = finis_col - start_col;
3424d522f475Smrg				if (best_len < test) {
3425d522f475Smrg				    best_len = test;
3426d522f475Smrg				    best_col = start_col;
3427d522f475Smrg				    TRACE(("match column %d len %d\n",
3428d522f475Smrg					   best_col,
3429d522f475Smrg					   best_len));
3430d522f475Smrg				}
3431d522f475Smrg			    }
3432d522f475Smrg			}
3433d522f475Smrg		    }
3434d522f475Smrg		    if (best_col >= 0) {
3435d522f475Smrg			int best_nxt = best_col + best_len;
3436d522f475Smrg			columnToCell(screen, firstRow, best_col, startc);
3437d522f475Smrg			columnToCell(screen, firstRow, best_nxt, endc);
3438d522f475Smrg			TRACE(("search::%s\n", search));
3439d522f475Smrg			TRACE(("indexed:%d..%d -> %d..%d\n",
3440d522f475Smrg			       best_col, best_nxt,
3441d522f475Smrg			       indexed[best_col],
3442d522f475Smrg			       indexed[best_nxt]));
3443d522f475Smrg			TRACE(("matched:%d:%s\n",
3444d522f475Smrg			       indexed[best_nxt] + 1 -
3445d522f475Smrg			       indexed[best_col],
3446956cc18dSsnj			       visibleChars((Char *) (search + indexed[best_col]),
3447d522f475Smrg					    (unsigned) (indexed[best_nxt] +
3448d522f475Smrg							1 -
3449d522f475Smrg							indexed[best_col]))));
3450d522f475Smrg		    }
3451d522f475Smrg		    free(search);
3452d522f475Smrg		}
3453d522f475Smrg		free(indexed);
3454956cc18dSsnj#if OPT_DEC_CHRSET
3455956cc18dSsnj		if ((ld = GET_LINEDATA(screen, startc->row)) != 0) {
3456956cc18dSsnj		    if (CSET_DOUBLE(GetLineDblCS(ld)))
3457956cc18dSsnj			startc->col *= 2;
3458956cc18dSsnj		}
3459956cc18dSsnj		if ((ld = GET_LINEDATA(screen, endc->row)) != 0) {
3460956cc18dSsnj		    if (CSET_DOUBLE(GetLineDblCS(ld)))
3461956cc18dSsnj			endc->col *= 2;
3462956cc18dSsnj		}
3463956cc18dSsnj#endif
3464d522f475Smrg	    }
3465d522f475Smrg	    regfree(&preg);
3466d522f475Smrg	}
3467d522f475Smrg    }
3468d522f475Smrg}
3469d522f475Smrg#endif /* OPT_SELECT_REGEX */
3470d522f475Smrg
3471956cc18dSsnj#define InitRow(name) \
3472956cc18dSsnj	ld.name = GET_LINEDATA(screen, screen->name.row)
3473956cc18dSsnj
3474956cc18dSsnj#define NextRow(name) \
3475956cc18dSsnj	ld.name = GET_LINEDATA(screen, ++screen->name.row)
3476956cc18dSsnj
3477956cc18dSsnj#define PrevRow(name) \
3478956cc18dSsnj	ld.name = GET_LINEDATA(screen, --screen->name.row)
3479956cc18dSsnj
348020d2c4d2Smrg#define MoreRows(name) \
348120d2c4d2Smrg	(screen->name.row < screen->max_row)
348220d2c4d2Smrg
3483956cc18dSsnj#define isPrevWrapped(name) \
3484956cc18dSsnj	(screen->name.row > 0 \
3485956cc18dSsnj	   && (ltmp = GET_LINEDATA(screen, screen->name.row - 1)) != 0 \
3486956cc18dSsnj	   && LineTstWrapped(ltmp))
3487956cc18dSsnj
3488d522f475Smrg/*
3489d522f475Smrg * sets startSel endSel
3490d522f475Smrg * ensuring that they have legal values
3491d522f475Smrg */
3492d522f475Smrgstatic void
3493d522f475SmrgComputeSelect(XtermWidget xw,
3494e0a2b6dfSmrg	      CELL *startc,
3495e0a2b6dfSmrg	      CELL *endc,
3496d522f475Smrg	      Bool extend)
3497d522f475Smrg{
3498956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3499956cc18dSsnj
3500d522f475Smrg    int cclass;
3501d522f475Smrg    CELL first = *startc;
3502d522f475Smrg    CELL last = *endc;
3503956cc18dSsnj    Boolean ignored = False;
3504956cc18dSsnj
3505956cc18dSsnj    struct {
3506956cc18dSsnj	LineData *startSel;
3507956cc18dSsnj	LineData *endSel;
3508956cc18dSsnj    } ld;
3509956cc18dSsnj    LineData *ltmp;
3510d522f475Smrg
3511d522f475Smrg    TRACE(("ComputeSelect(startRow=%d, startCol=%d, endRow=%d, endCol=%d, %sextend)\n",
3512d522f475Smrg	   first.row, first.col,
3513d522f475Smrg	   last.row, last.col,
3514d522f475Smrg	   extend ? "" : "no"));
3515d522f475Smrg
3516d522f475Smrg#if OPT_WIDE_CHARS
3517d522f475Smrg    if (first.col > 1
3518d522f475Smrg	&& isWideCell(first.row, first.col - 1)
3519d522f475Smrg	&& XTERM_CELL(first.row, first.col - 0) == HIDDEN_CHAR) {
352020d2c4d2Smrg	TRACE(("Adjusting start. Changing downwards from %i.\n", first.col));
3521d522f475Smrg	first.col -= 1;
3522d522f475Smrg	if (last.col == (first.col + 1))
3523d522f475Smrg	    last.col--;
3524d522f475Smrg    }
3525d522f475Smrg
3526d522f475Smrg    if (last.col > 1
3527d522f475Smrg	&& isWideCell(last.row, last.col - 1)
3528d522f475Smrg	&& XTERM_CELL(last.row, last.col) == HIDDEN_CHAR) {
3529d522f475Smrg	last.col += 1;
3530d522f475Smrg    }
3531d522f475Smrg#endif
3532d522f475Smrg
3533d522f475Smrg    if (Coordinate(screen, &first) <= Coordinate(screen, &last)) {
3534d522f475Smrg	screen->startSel = screen->startRaw = first;
3535d522f475Smrg	screen->endSel = screen->endRaw = last;
3536d522f475Smrg    } else {			/* Swap them */
3537d522f475Smrg	screen->startSel = screen->startRaw = last;
3538d522f475Smrg	screen->endSel = screen->endRaw = first;
3539d522f475Smrg    }
3540d522f475Smrg
3541956cc18dSsnj    InitRow(startSel);
3542956cc18dSsnj    InitRow(endSel);
3543956cc18dSsnj
3544d522f475Smrg    switch (screen->selectUnit) {
3545d522f475Smrg    case Select_CHAR:
3546956cc18dSsnj	(void) okPosition(screen, &(ld.startSel), &(screen->startSel));
3547956cc18dSsnj	(void) okPosition(screen, &(ld.endSel), &(screen->endSel));
3548d522f475Smrg	break;
3549d522f475Smrg
3550d522f475Smrg    case Select_WORD:
3551d522f475Smrg	TRACE(("Select_WORD\n"));
3552956cc18dSsnj	if (okPosition(screen, &(ld.startSel), &(screen->startSel))) {
3553956cc18dSsnj	    cclass = CClassOf(startSel);
3554d522f475Smrg	    do {
3555d522f475Smrg		--screen->startSel.col;
3556956cc18dSsnj		if (screen->startSel.col < 0
3557956cc18dSsnj		    && isPrevWrapped(startSel)) {
3558956cc18dSsnj		    PrevRow(startSel);
3559956cc18dSsnj		    screen->startSel.col = LastTextCol(screen, ld.startSel, screen->startSel.row);
3560d522f475Smrg		}
3561d522f475Smrg	    } while (screen->startSel.col >= 0
3562956cc18dSsnj		     && CClassSelects(startSel, cclass));
3563d522f475Smrg	    ++screen->startSel.col;
3564d522f475Smrg	}
3565d522f475Smrg#if OPT_WIDE_CHARS
3566d522f475Smrg	if (screen->startSel.col
3567d522f475Smrg	    && XTERM_CELL(screen->startSel.row,
3568d522f475Smrg			  screen->startSel.col) == HIDDEN_CHAR)
3569d522f475Smrg	    screen->startSel.col++;
3570d522f475Smrg#endif
3571d522f475Smrg
3572956cc18dSsnj	if (okPosition(screen, &(ld.endSel), &(screen->endSel))) {
35732e4f8982Smrg	    int length = LastTextCol(screen, ld.endSel, screen->endSel.row);
3574956cc18dSsnj	    cclass = CClassOf(endSel);
3575d522f475Smrg	    do {
3576d522f475Smrg		++screen->endSel.col;
3577d522f475Smrg		if (screen->endSel.col > length
3578956cc18dSsnj		    && LineTstWrapped(ld.endSel)) {
357920d2c4d2Smrg		    if (!MoreRows(endSel))
358020d2c4d2Smrg			break;
3581d522f475Smrg		    screen->endSel.col = 0;
3582956cc18dSsnj		    NextRow(endSel);
3583956cc18dSsnj		    length = LastTextCol(screen, ld.endSel, screen->endSel.row);
3584d522f475Smrg		}
3585d522f475Smrg	    } while (screen->endSel.col <= length
3586956cc18dSsnj		     && CClassSelects(endSel, cclass));
3587d522f475Smrg	    /* Word-select selects if pointing to any char in "word",
3588d522f475Smrg	     * especially note that it includes the last character in a word.
3589d522f475Smrg	     * So we do no --endSel.col and do special eol handling.
3590d522f475Smrg	     */
359120d2c4d2Smrg	    if (screen->endSel.col > length + 1
359220d2c4d2Smrg		&& MoreRows(endSel)) {
3593d522f475Smrg		screen->endSel.col = 0;
3594956cc18dSsnj		NextRow(endSel);
3595d522f475Smrg	    }
3596d522f475Smrg	}
3597d522f475Smrg#if OPT_WIDE_CHARS
3598d522f475Smrg	if (screen->endSel.col
3599d522f475Smrg	    && XTERM_CELL(screen->endSel.row,
3600d522f475Smrg			  screen->endSel.col) == HIDDEN_CHAR)
3601d522f475Smrg	    screen->endSel.col++;
3602d522f475Smrg#endif
3603d522f475Smrg
3604d522f475Smrg	screen->saveStartW = screen->startSel;
3605d522f475Smrg	break;
3606d522f475Smrg
3607d522f475Smrg    case Select_LINE:
3608d522f475Smrg	TRACE(("Select_LINE\n"));
360920d2c4d2Smrg	while (LineTstWrapped(ld.endSel)
361020d2c4d2Smrg	       && MoreRows(endSel)) {
3611956cc18dSsnj	    NextRow(endSel);
3612d522f475Smrg	}
3613d522f475Smrg	if (screen->cutToBeginningOfLine
3614d522f475Smrg	    || screen->startSel.row < screen->saveStartW.row) {
3615d522f475Smrg	    screen->startSel.col = 0;
3616956cc18dSsnj	    while (isPrevWrapped(startSel)) {
3617956cc18dSsnj		PrevRow(startSel);
3618d522f475Smrg	    }
3619d522f475Smrg	} else if (!extend) {
3620d522f475Smrg	    if ((first.row < screen->saveStartW.row)
3621d522f475Smrg		|| (isSameRow(&first, &(screen->saveStartW))
3622d522f475Smrg		    && first.col < screen->saveStartW.col)) {
3623d522f475Smrg		screen->startSel.col = 0;
3624956cc18dSsnj		while (isPrevWrapped(startSel)) {
3625956cc18dSsnj		    PrevRow(startSel);
3626d522f475Smrg		}
3627d522f475Smrg	    } else {
3628d522f475Smrg		screen->startSel = screen->saveStartW;
3629d522f475Smrg	    }
3630d522f475Smrg	}
3631956cc18dSsnj	trimLastLine(screen, &(ld.endSel), &(screen->endSel));
3632d522f475Smrg	break;
3633d522f475Smrg
3634d522f475Smrg    case Select_GROUP:		/* paragraph */
3635d522f475Smrg	TRACE(("Select_GROUP\n"));
3636956cc18dSsnj	if (okPosition(screen, &(ld.startSel), &(screen->startSel))) {
3637d522f475Smrg	    /* scan backward for beginning of group */
3638d522f475Smrg	    while (screen->startSel.row > 0 &&
3639956cc18dSsnj		   (LastTextCol(screen, ld.startSel, screen->startSel.row -
3640956cc18dSsnj				1) > 0 ||
3641956cc18dSsnj		    isPrevWrapped(startSel))) {
3642956cc18dSsnj		PrevRow(startSel);
3643d522f475Smrg	    }
3644d522f475Smrg	    screen->startSel.col = 0;
3645d522f475Smrg	    /* scan forward for end of group */
364620d2c4d2Smrg	    while (MoreRows(endSel) &&
3647956cc18dSsnj		   (LastTextCol(screen, ld.endSel, screen->endSel.row + 1) >
3648956cc18dSsnj		    0 ||
3649956cc18dSsnj		    LineTstWrapped(ld.endSel))) {
3650956cc18dSsnj		NextRow(endSel);
3651d522f475Smrg	    }
3652956cc18dSsnj	    trimLastLine(screen, &(ld.endSel), &(screen->endSel));
3653d522f475Smrg	}
3654d522f475Smrg	break;
3655d522f475Smrg
3656d522f475Smrg    case Select_PAGE:		/* everything one can see */
3657d522f475Smrg	TRACE(("Select_PAGE\n"));
3658d522f475Smrg	screen->startSel.row = 0;
3659d522f475Smrg	screen->startSel.col = 0;
366020d2c4d2Smrg	screen->endSel.row = MaxRows(screen);
3661d522f475Smrg	screen->endSel.col = 0;
3662d522f475Smrg	break;
3663d522f475Smrg
3664d522f475Smrg    case Select_ALL:		/* counts scrollback if in normal screen */
3665d522f475Smrg	TRACE(("Select_ALL\n"));
3666d522f475Smrg	screen->startSel.row = -screen->savedlines;
3667d522f475Smrg	screen->startSel.col = 0;
366820d2c4d2Smrg	screen->endSel.row = MaxRows(screen);
3669d522f475Smrg	screen->endSel.col = 0;
3670d522f475Smrg	break;
3671d522f475Smrg
3672d522f475Smrg#if OPT_SELECT_REGEX
3673d522f475Smrg    case Select_REGEX:
3674d522f475Smrg	do_select_regex(screen, &(screen->startSel), &(screen->endSel));
3675d522f475Smrg	break;
3676d522f475Smrg#endif
3677d522f475Smrg
3678d522f475Smrg    case NSELECTUNITS:		/* always ignore */
3679956cc18dSsnj	ignored = True;
3680956cc18dSsnj	break;
3681d522f475Smrg    }
3682d522f475Smrg
3683956cc18dSsnj    if (!ignored) {
3684956cc18dSsnj	/* check boundaries */
3685956cc18dSsnj	ScrollSelection(screen, 0, False);
3686956cc18dSsnj	TrackText(xw, &(screen->startSel), &(screen->endSel));
3687956cc18dSsnj    }
3688d522f475Smrg
3689d522f475Smrg    return;
3690d522f475Smrg}
3691d522f475Smrg
3692d522f475Smrg/* Guaranteed (first.row, first.col) <= (last.row, last.col) */
3693d522f475Smrgstatic void
3694d522f475SmrgTrackText(XtermWidget xw,
3695e0a2b6dfSmrg	  const CELL *firstp,
3696e0a2b6dfSmrg	  const CELL *lastp)
3697d522f475Smrg{
3698956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3699d522f475Smrg    int from, to;
3700d522f475Smrg    CELL old_start, old_end;
3701d522f475Smrg    CELL first = *firstp;
3702d522f475Smrg    CELL last = *lastp;
3703d522f475Smrg
3704d522f475Smrg    TRACE(("TrackText(first=%d,%d, last=%d,%d)\n",
3705d522f475Smrg	   first.row, first.col, last.row, last.col));
3706d522f475Smrg
3707d522f475Smrg    old_start = screen->startH;
3708d522f475Smrg    old_end = screen->endH;
37090bd37d32Smrg    TRACE(("...previous(first=%d,%d, last=%d,%d)\n",
37100bd37d32Smrg	   old_start.row, old_start.col,
37110bd37d32Smrg	   old_end.row, old_end.col));
3712d522f475Smrg    if (isSameCELL(&first, &old_start) &&
37130bd37d32Smrg	isSameCELL(&last, &old_end)) {
3714d522f475Smrg	return;
37150bd37d32Smrg    }
37160bd37d32Smrg
3717d522f475Smrg    screen->startH = first;
3718d522f475Smrg    screen->endH = last;
3719d522f475Smrg    from = Coordinate(screen, &screen->startH);
3720d522f475Smrg    to = Coordinate(screen, &screen->endH);
3721d522f475Smrg    if (to <= screen->startHCoord || from > screen->endHCoord) {
3722d522f475Smrg	/* No overlap whatsoever between old and new hilite */
3723d522f475Smrg	ReHiliteText(xw, &old_start, &old_end);
3724d522f475Smrg	ReHiliteText(xw, &first, &last);
3725d522f475Smrg    } else {
3726d522f475Smrg	if (from < screen->startHCoord) {
3727d522f475Smrg	    /* Extend left end */
3728d522f475Smrg	    ReHiliteText(xw, &first, &old_start);
3729d522f475Smrg	} else if (from > screen->startHCoord) {
3730d522f475Smrg	    /* Shorten left end */
3731d522f475Smrg	    ReHiliteText(xw, &old_start, &first);
3732d522f475Smrg	}
3733d522f475Smrg	if (to > screen->endHCoord) {
3734d522f475Smrg	    /* Extend right end */
3735d522f475Smrg	    ReHiliteText(xw, &old_end, &last);
3736d522f475Smrg	} else if (to < screen->endHCoord) {
3737d522f475Smrg	    /* Shorten right end */
3738d522f475Smrg	    ReHiliteText(xw, &last, &old_end);
3739d522f475Smrg	}
3740d522f475Smrg    }
3741d522f475Smrg    screen->startHCoord = from;
3742d522f475Smrg    screen->endHCoord = to;
3743d522f475Smrg}
3744d522f475Smrg
3745d522f475Smrg/* Guaranteed that (first->row, first->col) <= (last->row, last->col) */
3746d522f475Smrgstatic void
3747d522f475SmrgReHiliteText(XtermWidget xw,
3748e0a2b6dfSmrg	     CELL *firstp,
3749e0a2b6dfSmrg	     CELL *lastp)
3750d522f475Smrg{
3751956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3752d522f475Smrg    CELL first = *firstp;
3753d522f475Smrg    CELL last = *lastp;
3754d522f475Smrg
3755d522f475Smrg    TRACE(("ReHiliteText from %d.%d to %d.%d\n",
3756d522f475Smrg	   first.row, first.col, last.row, last.col));
3757d522f475Smrg
3758d522f475Smrg    if (first.row < 0)
3759d522f475Smrg	first.row = first.col = 0;
3760d522f475Smrg    else if (first.row > screen->max_row)
3761d522f475Smrg	return;			/* nothing to do, since last.row >= first.row */
3762d522f475Smrg
3763d522f475Smrg    if (last.row < 0)
3764d522f475Smrg	return;			/* nothing to do, since first.row <= last.row */
3765d522f475Smrg    else if (last.row > screen->max_row) {
3766d522f475Smrg	last.row = screen->max_row;
3767d522f475Smrg	last.col = MaxCols(screen);
3768d522f475Smrg    }
3769d522f475Smrg    if (isSameCELL(&first, &last))
3770d522f475Smrg	return;
3771d522f475Smrg
3772d522f475Smrg    if (!isSameRow(&first, &last)) {	/* do multiple rows */
37732e4f8982Smrg	int i;
3774d522f475Smrg	if ((i = screen->max_col - first.col + 1) > 0) {	/* first row */
3775d522f475Smrg	    ScrnRefresh(xw, first.row, first.col, 1, i, True);
3776d522f475Smrg	}
3777d522f475Smrg	if ((i = last.row - first.row - 1) > 0) {	/* middle rows */
3778d522f475Smrg	    ScrnRefresh(xw, first.row + 1, 0, i, MaxCols(screen), True);
3779d522f475Smrg	}
3780d522f475Smrg	if (last.col > 0 && last.row <= screen->max_row) {	/* last row */
3781d522f475Smrg	    ScrnRefresh(xw, last.row, 0, 1, last.col, True);
3782d522f475Smrg	}
3783d522f475Smrg    } else {			/* do single row */
3784d522f475Smrg	ScrnRefresh(xw, first.row, first.col, 1, last.col - first.col, True);
3785d522f475Smrg    }
3786d522f475Smrg}
3787d522f475Smrg
3788d522f475Smrg/*
3789913cc679Smrg * Guaranteed that (cellc->row, cellc->col) <= (cell->row, cell->col),
3790913cc679Smrg * and that both points are valid
3791d522f475Smrg * (may have cell->row = screen->max_row+1, cell->col = 0).
3792d522f475Smrg */
3793d522f475Smrgstatic void
3794d522f475SmrgSaltTextAway(XtermWidget xw,
3795e0a2b6dfSmrg	     CELL *cellc,
3796e0a2b6dfSmrg	     CELL *cell)
3797d522f475Smrg{
3798956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3799d522f475Smrg    int i, j = 0;
3800d522f475Smrg    int eol;
3801d522f475Smrg    Char *line;
3802d522f475Smrg    Char *lp;
3803d522f475Smrg    CELL first = *cellc;
3804d522f475Smrg    CELL last = *cell;
3805d522f475Smrg
3806d522f475Smrg    if (isSameRow(&first, &last) && first.col > last.col) {
38072e4f8982Smrg	int tmp;
3808956cc18dSsnj	EXCHANGE(first.col, last.col, tmp);
3809d522f475Smrg    }
3810d522f475Smrg
3811d522f475Smrg    --last.col;
3812d522f475Smrg    /* first we need to know how long the string is before we can save it */
3813d522f475Smrg
3814d522f475Smrg    if (isSameRow(&last, &first)) {
3815d522f475Smrg	j = Length(screen, first.row, first.col, last.col);
3816d522f475Smrg    } else {			/* two cases, cut is on same line, cut spans multiple lines */
3817d522f475Smrg	j += Length(screen, first.row, first.col, screen->max_col) + 1;
3818d522f475Smrg	for (i = first.row + 1; i < last.row; i++)
3819d522f475Smrg	    j += Length(screen, i, 0, screen->max_col) + 1;
3820d522f475Smrg	if (last.col >= 0)
3821d522f475Smrg	    j += Length(screen, last.row, 0, last.col);
3822d522f475Smrg    }
3823d522f475Smrg
3824d522f475Smrg    /* UTF-8 may require more space */
3825d522f475Smrg    if_OPT_WIDE_CHARS(screen, {
3826d522f475Smrg	j *= 4;
3827d522f475Smrg    });
3828d522f475Smrg
3829d522f475Smrg    /* now get some memory to save it in */
3830d522f475Smrg
3831d522f475Smrg    if (screen->selection_size <= j) {
383220d2c4d2Smrg	if ((line = (Char *) malloc((size_t) j + 1)) == 0)
3833d522f475Smrg	    SysError(ERROR_BMALLOC2);
3834d522f475Smrg	XtFree((char *) screen->selection_data);
3835d522f475Smrg	screen->selection_data = line;
3836d522f475Smrg	screen->selection_size = j + 1;
3837d522f475Smrg    } else {
3838d522f475Smrg	line = screen->selection_data;
3839d522f475Smrg    }
3840d522f475Smrg
3841d522f475Smrg    if ((line == 0)
3842d522f475Smrg	|| (j < 0))
3843d522f475Smrg	return;
3844d522f475Smrg
3845d522f475Smrg    line[j] = '\0';		/* make sure it is null terminated */
3846d522f475Smrg    lp = line;			/* lp points to where to save the text */
3847d522f475Smrg    if (isSameRow(&last, &first)) {
3848d522f475Smrg	lp = SaveText(screen, last.row, first.col, last.col, lp, &eol);
3849d522f475Smrg    } else {
3850d522f475Smrg	lp = SaveText(screen, first.row, first.col, screen->max_col, lp, &eol);
3851d522f475Smrg	if (eol)
3852d522f475Smrg	    *lp++ = '\n';	/* put in newline at end of line */
3853d522f475Smrg	for (i = first.row + 1; i < last.row; i++) {
3854d522f475Smrg	    lp = SaveText(screen, i, 0, screen->max_col, lp, &eol);
3855d522f475Smrg	    if (eol)
3856d522f475Smrg		*lp++ = '\n';
3857d522f475Smrg	}
3858d522f475Smrg	if (last.col >= 0)
3859d522f475Smrg	    lp = SaveText(screen, last.row, 0, last.col, lp, &eol);
3860d522f475Smrg    }
3861d522f475Smrg    *lp = '\0';			/* make sure we have end marked */
3862d522f475Smrg
3863956cc18dSsnj    TRACE(("Salted TEXT:%d:%s\n", (int) (lp - line),
3864956cc18dSsnj	   visibleChars(line, (unsigned) (lp - line))));
3865d522f475Smrg
38662eaa94a1Schristos    screen->selection_length = (unsigned long) (lp - line);
3867d522f475Smrg}
3868d522f475Smrg
3869d522f475Smrg#if OPT_PASTE64
3870d522f475Smrgvoid
3871e0a2b6dfSmrgClearSelectionBuffer(TScreen *screen)
3872d522f475Smrg{
3873d522f475Smrg    screen->selection_length = 0;
3874d522f475Smrg    screen->base64_count = 0;
3875d522f475Smrg}
3876d522f475Smrg
3877d522f475Smrgstatic void
3878e0a2b6dfSmrgAppendStrToSelectionBuffer(TScreen *screen, Char *text, size_t len)
3879d522f475Smrg{
3880d522f475Smrg    if (len != 0) {
38812eaa94a1Schristos	int j = (int) (screen->selection_length + len);		/* New length */
3882d522f475Smrg	int k = j + (j >> 2) + 80;	/* New size if we grow buffer: grow by ~50% */
3883d522f475Smrg	if (j + 1 >= screen->selection_size) {
3884d522f475Smrg	    if (!screen->selection_length) {
3885d522f475Smrg		/* New buffer */
3886d522f475Smrg		Char *line;
388720d2c4d2Smrg		if ((line = (Char *) malloc((size_t) k)) == 0)
3888d522f475Smrg		    SysError(ERROR_BMALLOC2);
3889d522f475Smrg		XtFree((char *) screen->selection_data);
3890d522f475Smrg		screen->selection_data = line;
3891d522f475Smrg	    } else {
3892d522f475Smrg		/* Realloc buffer */
3893d522f475Smrg		screen->selection_data = (Char *)
3894d522f475Smrg		    realloc(screen->selection_data,
389520d2c4d2Smrg			    (size_t) k);
3896d522f475Smrg		if (screen->selection_data == 0)
3897d522f475Smrg		    SysError(ERROR_BMALLOC2);
3898d522f475Smrg	    }
3899d522f475Smrg	    screen->selection_size = k;
3900d522f475Smrg	}
390120d2c4d2Smrg	if (screen->selection_data != 0) {
390220d2c4d2Smrg	    memcpy(screen->selection_data + screen->selection_length, text, len);
390320d2c4d2Smrg	    screen->selection_length += len;
390420d2c4d2Smrg	    screen->selection_data[screen->selection_length] = 0;
390520d2c4d2Smrg	}
3906d522f475Smrg    }
3907d522f475Smrg}
3908d522f475Smrg
3909d522f475Smrgvoid
3910e0a2b6dfSmrgAppendToSelectionBuffer(TScreen *screen, unsigned c)
3911d522f475Smrg{
39122eaa94a1Schristos    unsigned six;
3913d522f475Smrg    Char ch;
3914d522f475Smrg
3915d522f475Smrg    /* Decode base64 character */
3916d522f475Smrg    if (c >= 'A' && c <= 'Z')
3917d522f475Smrg	six = c - 'A';
3918d522f475Smrg    else if (c >= 'a' && c <= 'z')
3919d522f475Smrg	six = c - 'a' + 26;
3920d522f475Smrg    else if (c >= '0' && c <= '9')
3921d522f475Smrg	six = c - '0' + 52;
3922d522f475Smrg    else if (c == '+')
3923d522f475Smrg	six = 62;
3924d522f475Smrg    else if (c == '/')
3925d522f475Smrg	six = 63;
3926d522f475Smrg    else
3927d522f475Smrg	return;
3928d522f475Smrg
3929d522f475Smrg    /* Accumulate bytes */
3930d522f475Smrg    switch (screen->base64_count) {
3931d522f475Smrg    case 0:
3932d522f475Smrg	screen->base64_accu = six;
3933d522f475Smrg	screen->base64_count = 6;
3934d522f475Smrg	break;
3935d522f475Smrg
3936d522f475Smrg    case 2:
39372eaa94a1Schristos	ch = CharOf((screen->base64_accu << 6) + six);
3938d522f475Smrg	screen->base64_count = 0;
393920d2c4d2Smrg	AppendStrToSelectionBuffer(screen, &ch, (size_t) 1);
3940d522f475Smrg	break;
3941d522f475Smrg
3942d522f475Smrg    case 4:
39432eaa94a1Schristos	ch = CharOf((screen->base64_accu << 4) + (six >> 2));
3944d522f475Smrg	screen->base64_accu = (six & 0x3);
3945d522f475Smrg	screen->base64_count = 2;
394620d2c4d2Smrg	AppendStrToSelectionBuffer(screen, &ch, (size_t) 1);
3947d522f475Smrg	break;
3948d522f475Smrg
3949d522f475Smrg    case 6:
39502eaa94a1Schristos	ch = CharOf((screen->base64_accu << 2) + (six >> 4));
3951d522f475Smrg	screen->base64_accu = (six & 0xF);
3952d522f475Smrg	screen->base64_count = 4;
395320d2c4d2Smrg	AppendStrToSelectionBuffer(screen, &ch, (size_t) 1);
3954d522f475Smrg	break;
3955d522f475Smrg    }
3956d522f475Smrg}
3957d522f475Smrg
3958d522f475Smrgvoid
3959e0a2b6dfSmrgCompleteSelection(XtermWidget xw, String *args, Cardinal len)
3960d522f475Smrg{
3961956cc18dSsnj    TScreen *screen = TScreenOf(xw);
3962956cc18dSsnj
3963956cc18dSsnj    screen->base64_count = 0;
3964956cc18dSsnj    screen->base64_accu = 0;
3965d522f475Smrg    _OwnSelection(xw, args, len);
3966d522f475Smrg}
3967d522f475Smrg#endif /* OPT_PASTE64 */
3968d522f475Smrg
3969d522f475Smrgstatic Bool
3970d522f475Smrg_ConvertSelectionHelper(Widget w,
3971894e0ac8Smrg			Atom *type,
3972d522f475Smrg			XtPointer *value,
3973d522f475Smrg			unsigned long *length,
39742e4f8982Smrg			Char *data,
39752e4f8982Smrg			unsigned long remaining,
3976d522f475Smrg			int *format,
3977d522f475Smrg			int (*conversion_function) (Display *,
3978d522f475Smrg						    char **, int,
3979d522f475Smrg						    XICCEncodingStyle,
3980d522f475Smrg						    XTextProperty *),
3981d522f475Smrg			XICCEncodingStyle conversion_style)
3982d522f475Smrg{
3983956cc18dSsnj    XtermWidget xw;
3984956cc18dSsnj
398501037d57Smrg    *value = 0;
398601037d57Smrg    *length = 0;
398701037d57Smrg    *type = 0;
398801037d57Smrg    *format = 0;
398901037d57Smrg
3990956cc18dSsnj    if ((xw = getXtermWidget(w)) != 0) {
3991956cc18dSsnj	TScreen *screen = TScreenOf(xw);
3992d522f475Smrg	Display *dpy = XtDisplay(w);
3993d522f475Smrg	XTextProperty textprop;
399401037d57Smrg	int out_n = 0;
399501037d57Smrg	char *result = 0;
39962e4f8982Smrg	char *the_data = (char *) data;
399701037d57Smrg	char *the_next;
399801037d57Smrg
399901037d57Smrg	TRACE(("converting %ld:'%s'\n",
400001037d57Smrg	       (long) screen->selection_length,
400101037d57Smrg	       visibleChars(screen->selection_data, (unsigned) screen->selection_length)));
400201037d57Smrg	/*
400301037d57Smrg	 * For most selections, we can convert in one pass.  It is possible
400401037d57Smrg	 * that some applications contain embedded nulls, e.g., using xterm's
400501037d57Smrg	 * paste64 feature.  For those cases, we will build up the result in
400601037d57Smrg	 * parts.
400701037d57Smrg	 */
400801037d57Smrg	if (memchr(the_data, 0, screen->selection_length) != 0) {
400901037d57Smrg	    TRACE(("selection contains embedded nulls\n"));
401001037d57Smrg	    result = calloc(screen->selection_length + 1, sizeof(char));
401101037d57Smrg	}
4012d522f475Smrg
401301037d57Smrg      next_try:
401401037d57Smrg	memset(&textprop, 0, sizeof(textprop));
4015d522f475Smrg	if (conversion_function(dpy, &the_data, 1,
4016d522f475Smrg				conversion_style,
4017d522f475Smrg				&textprop) >= Success) {
401801037d57Smrg	    if ((result != 0)
401901037d57Smrg		&& (textprop.value != 0)
402001037d57Smrg		&& (textprop.format == 8)) {
402101037d57Smrg		char *text_values = (char *) textprop.value;
402201037d57Smrg		unsigned long in_n;
402301037d57Smrg
402401037d57Smrg		if (out_n == 0) {
402501037d57Smrg		    *value = result;
402601037d57Smrg		    *type = textprop.encoding;
402701037d57Smrg		    *format = textprop.format;
402801037d57Smrg		}
402901037d57Smrg		for (in_n = 0; in_n < textprop.nitems; ++in_n) {
403001037d57Smrg		    result[out_n++] = text_values[in_n];
403101037d57Smrg		}
403201037d57Smrg		*length += textprop.nitems;
403301037d57Smrg		if ((the_next = memchr(the_data, 0, remaining)) != 0) {
403401037d57Smrg		    unsigned long this_was = (unsigned long) (the_next - the_data);
403501037d57Smrg		    this_was++;
403601037d57Smrg		    the_data += this_was;
403701037d57Smrg		    remaining -= this_was;
403801037d57Smrg		    result[out_n++] = 0;
403901037d57Smrg		    *length += 1;
404001037d57Smrg		    if (remaining)
404101037d57Smrg			goto next_try;
404201037d57Smrg		}
404301037d57Smrg		return True;
404401037d57Smrg	    } else {
404501037d57Smrg		free(result);
404601037d57Smrg		*value = (XtPointer) textprop.value;
404701037d57Smrg		*length = textprop.nitems;
404801037d57Smrg		*type = textprop.encoding;
404901037d57Smrg		*format = textprop.format;
405001037d57Smrg		return True;
405101037d57Smrg	    }
4052d522f475Smrg	}
405301037d57Smrg	free(result);
4054d522f475Smrg    }
4055d522f475Smrg    return False;
4056d522f475Smrg}
4057d522f475Smrg
40582eaa94a1Schristosstatic Boolean
40592eaa94a1SchristosSaveConvertedLength(XtPointer *target, unsigned long source)
40602eaa94a1Schristos{
40612eaa94a1Schristos    Boolean result = False;
40622eaa94a1Schristos
40632eaa94a1Schristos    *target = XtMalloc(4);
40642eaa94a1Schristos    if (*target != 0) {
40652eaa94a1Schristos	result = True;
40662eaa94a1Schristos	if (sizeof(unsigned long) == 4) {
40672eaa94a1Schristos	    *(unsigned long *) *target = source;
40682eaa94a1Schristos	} else if (sizeof(unsigned) == 4) {
406920d2c4d2Smrg	    *(unsigned *) *target = (unsigned) source;
40702eaa94a1Schristos	} else if (sizeof(unsigned short) == 4) {
40712eaa94a1Schristos	    *(unsigned short *) *target = (unsigned short) source;
40722eaa94a1Schristos	} else {
40732eaa94a1Schristos	    /* FIXME - does this depend on byte-order? */
40742eaa94a1Schristos	    unsigned long temp = source;
407520d2c4d2Smrg	    memcpy((char *) *target,
407620d2c4d2Smrg		   ((char *) &temp) + sizeof(temp) - 4,
407720d2c4d2Smrg		   (size_t) 4);
40782eaa94a1Schristos	}
40792eaa94a1Schristos    }
40802eaa94a1Schristos    return result;
40812eaa94a1Schristos}
40822eaa94a1Schristos
40832e4f8982Smrg#define keepClipboard(atom) ((screen->keepClipboard) && \
40842e4f8982Smrg	 (atom == XInternAtom(screen->display, "CLIPBOARD", False)))
40852e4f8982Smrg
4086d522f475Smrgstatic Boolean
4087d522f475SmrgConvertSelection(Widget w,
4088894e0ac8Smrg		 Atom *selection,
4089894e0ac8Smrg		 Atom *target,
4090894e0ac8Smrg		 Atom *type,
4091d522f475Smrg		 XtPointer *value,
4092d522f475Smrg		 unsigned long *length,
4093d522f475Smrg		 int *format)
4094d522f475Smrg{
4095d522f475Smrg    Display *dpy = XtDisplay(w);
4096d522f475Smrg    TScreen *screen;
4097d522f475Smrg    Bool result = False;
4098d522f475Smrg
40992e4f8982Smrg    Char *data;
41002e4f8982Smrg    unsigned long data_length;
41012e4f8982Smrg
4102956cc18dSsnj    XtermWidget xw;
4103956cc18dSsnj
4104956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0)
4105d522f475Smrg	return False;
4106d522f475Smrg
4107956cc18dSsnj    screen = TScreenOf(xw);
4108d522f475Smrg
4109956cc18dSsnj    TRACE(("ConvertSelection %s\n",
4110956cc18dSsnj	   visibleSelectionTarget(dpy, *target)));
4111956cc18dSsnj
41122e4f8982Smrg    if (keepClipboard(*selection)) {
41132e4f8982Smrg	TRACE(("asked for clipboard\n"));
41142e4f8982Smrg	data = screen->clipboard_data;
41152e4f8982Smrg	data_length = screen->clipboard_size;
41162e4f8982Smrg    } else {
41172e4f8982Smrg	TRACE(("asked for selection\n"));
41182e4f8982Smrg	data = screen->selection_data;
41192e4f8982Smrg	data_length = screen->selection_length;
41202e4f8982Smrg    }
41212e4f8982Smrg
41222e4f8982Smrg    if (data == NULL) {
412301037d57Smrg	TRACE(("...FIXME: no selection_data\n"));
412401037d57Smrg	return False;		/* can this happen? */
412501037d57Smrg    }
412601037d57Smrg
4127d522f475Smrg    if (*target == XA_TARGETS(dpy)) {
4128d522f475Smrg	Atom *targetP;
4129d522f475Smrg	XPointer std_return = 0;
4130d522f475Smrg	unsigned long std_length;
4131d522f475Smrg
4132d522f475Smrg	if (XmuConvertStandardSelection(w, screen->selection_time, selection,
4133d522f475Smrg					target, type, &std_return,
4134d522f475Smrg					&std_length, format)) {
4135956cc18dSsnj	    Atom *my_targets = _SelectionTargets(w);
41362e4f8982Smrg	    Atom *allocP;
41372e4f8982Smrg	    Atom *std_targets;
4138956cc18dSsnj
4139956cc18dSsnj	    TRACE(("XmuConvertStandardSelection - success\n"));
4140a1f3da82Smrg	    std_targets = (Atom *) (void *) (std_return);
4141d522f475Smrg	    *length = std_length + 6;
4142d522f475Smrg
4143a1f3da82Smrg	    targetP = TypeXtMallocN(Atom, *length);
4144d522f475Smrg	    allocP = targetP;
4145d522f475Smrg
4146d522f475Smrg	    *value = (XtPointer) targetP;
4147d522f475Smrg
41480bd37d32Smrg	    if (my_targets != 0) {
41490bd37d32Smrg		while (*my_targets != None) {
41500bd37d32Smrg		    *targetP++ = *my_targets++;
41510bd37d32Smrg		}
4152956cc18dSsnj	    }
4153d522f475Smrg	    *targetP++ = XA_LENGTH(dpy);
4154d522f475Smrg	    *targetP++ = XA_LIST_LENGTH(dpy);
4155d522f475Smrg
41562eaa94a1Schristos	    *length = std_length + (unsigned long) (targetP - allocP);
4157d522f475Smrg
4158d522f475Smrg	    memcpy(targetP, std_targets, sizeof(Atom) * std_length);
4159d522f475Smrg	    XtFree((char *) std_targets);
4160d522f475Smrg	    *type = XA_ATOM;
4161d522f475Smrg	    *format = 32;
4162d522f475Smrg	    result = True;
4163956cc18dSsnj	} else {
4164956cc18dSsnj	    TRACE(("XmuConvertStandardSelection - failed\n"));
4165d522f475Smrg	}
4166d522f475Smrg    }
4167d522f475Smrg#if OPT_WIDE_CHARS
4168d522f475Smrg    else if (screen->wide_chars && *target == XA_STRING) {
4169d522f475Smrg	result =
4170d522f475Smrg	    _ConvertSelectionHelper(w,
41712e4f8982Smrg				    type, value, length, data,
41722e4f8982Smrg				    data_length, format,
4173d522f475Smrg				    Xutf8TextListToTextProperty,
4174d522f475Smrg				    XStringStyle);
4175956cc18dSsnj	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4176d522f475Smrg    } else if (screen->wide_chars && *target == XA_UTF8_STRING(dpy)) {
4177d522f475Smrg	result =
4178d522f475Smrg	    _ConvertSelectionHelper(w,
41792e4f8982Smrg				    type, value, length, data,
41802e4f8982Smrg				    data_length, format,
4181d522f475Smrg				    Xutf8TextListToTextProperty,
4182d522f475Smrg				    XUTF8StringStyle);
4183956cc18dSsnj	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4184d522f475Smrg    } else if (screen->wide_chars && *target == XA_TEXT(dpy)) {
4185d522f475Smrg	result =
4186d522f475Smrg	    _ConvertSelectionHelper(w,
41872e4f8982Smrg				    type, value, length, data,
41882e4f8982Smrg				    data_length, format,
4189d522f475Smrg				    Xutf8TextListToTextProperty,
4190d522f475Smrg				    XStdICCTextStyle);
4191956cc18dSsnj	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4192d522f475Smrg    } else if (screen->wide_chars && *target == XA_COMPOUND_TEXT(dpy)) {
4193d522f475Smrg	result =
4194d522f475Smrg	    _ConvertSelectionHelper(w,
41952e4f8982Smrg				    type, value, length, data,
41962e4f8982Smrg				    data_length, format,
4197d522f475Smrg				    Xutf8TextListToTextProperty,
4198d522f475Smrg				    XCompoundTextStyle);
4199956cc18dSsnj	TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4200d522f475Smrg    }
4201d522f475Smrg#endif
4202d522f475Smrg
4203d522f475Smrg    else if (*target == XA_STRING) {	/* not wide_chars */
4204d522f475Smrg	/* We can only reach this point if the selection requestor
4205d522f475Smrg	   requested STRING before any of TEXT, COMPOUND_TEXT or
4206d522f475Smrg	   UTF8_STRING.  We therefore assume that the requestor is not
4207d522f475Smrg	   properly internationalised, and dump raw eight-bit data
4208d522f475Smrg	   with no conversion into the selection.  Yes, this breaks
4209d522f475Smrg	   the ICCCM in non-Latin-1 locales. */
4210d522f475Smrg	*type = XA_STRING;
4211d522f475Smrg	*value = (XtPointer) screen->selection_data;
4212d522f475Smrg	*length = screen->selection_length;
4213d522f475Smrg	*format = 8;
4214d522f475Smrg	result = True;
4215956cc18dSsnj	TRACE(("...raw 8-bit data:%d\n", result));
4216d522f475Smrg    } else if (*target == XA_TEXT(dpy)) {	/* not wide_chars */
4217d522f475Smrg	result =
4218d522f475Smrg	    _ConvertSelectionHelper(w,
42192e4f8982Smrg				    type, value, length, data,
42202e4f8982Smrg				    data_length, format,
4221d522f475Smrg				    XmbTextListToTextProperty,
4222d522f475Smrg				    XStdICCTextStyle);
4223956cc18dSsnj	TRACE(("...XmbTextListToTextProperty(StdICC):%d\n", result));
4224d522f475Smrg    } else if (*target == XA_COMPOUND_TEXT(dpy)) {	/* not wide_chars */
4225d522f475Smrg	result =
4226d522f475Smrg	    _ConvertSelectionHelper(w,
42272e4f8982Smrg				    type, value, length, data,
42282e4f8982Smrg				    data_length, format,
4229d522f475Smrg				    XmbTextListToTextProperty,
4230d522f475Smrg				    XCompoundTextStyle);
4231956cc18dSsnj	TRACE(("...XmbTextListToTextProperty(Compound):%d\n", result));
4232d522f475Smrg    }
4233d522f475Smrg#ifdef X_HAVE_UTF8_STRING
4234d522f475Smrg    else if (*target == XA_UTF8_STRING(dpy)) {	/* not wide_chars */
4235d522f475Smrg	result =
4236d522f475Smrg	    _ConvertSelectionHelper(w,
42372e4f8982Smrg				    type, value, length, data,
42382e4f8982Smrg				    data_length, format,
4239d522f475Smrg				    XmbTextListToTextProperty,
4240d522f475Smrg				    XUTF8StringStyle);
4241956cc18dSsnj	TRACE(("...XmbTextListToTextProperty(UTF8):%d\n", result));
4242d522f475Smrg    }
4243d522f475Smrg#endif
4244d522f475Smrg    else if (*target == XA_LIST_LENGTH(dpy)) {
424520d2c4d2Smrg	result = SaveConvertedLength(value, (unsigned long) 1);
4246d522f475Smrg	*type = XA_INTEGER;
4247d522f475Smrg	*length = 1;
4248d522f475Smrg	*format = 32;
4249956cc18dSsnj	TRACE(("...list of values:%d\n", result));
4250d522f475Smrg    } else if (*target == XA_LENGTH(dpy)) {
4251d522f475Smrg	/* This value is wrong if we have UTF-8 text */
42522eaa94a1Schristos	result = SaveConvertedLength(value, screen->selection_length);
4253d522f475Smrg	*type = XA_INTEGER;
4254d522f475Smrg	*length = 1;
4255d522f475Smrg	*format = 32;
4256956cc18dSsnj	TRACE(("...list of values:%d\n", result));
4257d522f475Smrg    } else if (XmuConvertStandardSelection(w,
4258d522f475Smrg					   screen->selection_time, selection,
4259d522f475Smrg					   target, type, (XPointer *) value,
4260d522f475Smrg					   length, format)) {
4261d522f475Smrg	result = True;
4262956cc18dSsnj	TRACE(("...XmuConvertStandardSelection:%d\n", result));
4263d522f475Smrg    }
4264d522f475Smrg
4265d522f475Smrg    /* else */
42662eaa94a1Schristos    return (Boolean) result;
4267d522f475Smrg}
4268d522f475Smrg
4269d522f475Smrgstatic void
4270894e0ac8SmrgLoseSelection(Widget w, Atom *selection)
4271d522f475Smrg{
4272d522f475Smrg    TScreen *screen;
4273d522f475Smrg    Atom *atomP;
4274d522f475Smrg    Cardinal i;
4275d522f475Smrg
4276956cc18dSsnj    XtermWidget xw;
4277956cc18dSsnj
4278956cc18dSsnj    if ((xw = getXtermWidget(w)) == 0)
4279d522f475Smrg	return;
4280d522f475Smrg
4281956cc18dSsnj    screen = TScreenOf(xw);
4282913cc679Smrg    TRACE(("LoseSelection %s\n", TraceAtomName(screen->display, *selection)));
428301037d57Smrg
4284d522f475Smrg    for (i = 0, atomP = screen->selection_atoms;
4285d522f475Smrg	 i < screen->selection_count; i++, atomP++) {
4286d522f475Smrg	if (*selection == *atomP)
4287d522f475Smrg	    *atomP = (Atom) 0;
4288d522f475Smrg	if (CutBuffer(*atomP) >= 0) {
4289d522f475Smrg	    *atomP = (Atom) 0;
4290d522f475Smrg	}
4291d522f475Smrg    }
4292d522f475Smrg
4293d522f475Smrg    for (i = screen->selection_count; i; i--) {
4294d522f475Smrg	if (screen->selection_atoms[i - 1] != 0)
4295d522f475Smrg	    break;
4296d522f475Smrg    }
4297d522f475Smrg    screen->selection_count = i;
4298d522f475Smrg
4299d522f475Smrg    for (i = 0, atomP = screen->selection_atoms;
4300d522f475Smrg	 i < screen->selection_count; i++, atomP++) {
4301d522f475Smrg	if (*atomP == (Atom) 0) {
4302d522f475Smrg	    *atomP = screen->selection_atoms[--screen->selection_count];
4303d522f475Smrg	}
4304d522f475Smrg    }
4305d522f475Smrg
4306d522f475Smrg    if (screen->selection_count == 0)
4307956cc18dSsnj	TrackText(xw, &zeroCELL, &zeroCELL);
4308d522f475Smrg}
4309d522f475Smrg
4310d522f475Smrg/* ARGSUSED */
4311d522f475Smrgstatic void
4312d522f475SmrgSelectionDone(Widget w GCC_UNUSED,
4313894e0ac8Smrg	      Atom *selection GCC_UNUSED,
4314894e0ac8Smrg	      Atom *target GCC_UNUSED)
4315d522f475Smrg{
4316d522f475Smrg    /* empty proc so Intrinsics know we want to keep storage */
431701037d57Smrg    TRACE(("SelectionDone\n"));
4318d522f475Smrg}
4319d522f475Smrg
4320d522f475Smrgstatic void
4321d522f475Smrg_OwnSelection(XtermWidget xw,
4322e0a2b6dfSmrg	      String *selections,
4323d522f475Smrg	      Cardinal count)
4324d522f475Smrg{
4325956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4326d522f475Smrg    Atom *atoms = screen->selection_atoms;
4327d522f475Smrg    Cardinal i;
4328d522f475Smrg    Bool have_selection = False;
4329d522f475Smrg
433020d2c4d2Smrg    if (count == 0)
433120d2c4d2Smrg	return;
4332d522f475Smrg
433301037d57Smrg    TRACE(("_OwnSelection count %d, length %ld value %s\n", count,
433401037d57Smrg	   screen->selection_length,
433501037d57Smrg	   visibleChars(screen->selection_data, (unsigned) screen->selection_length)));
4336d522f475Smrg    selections = MapSelections(xw, selections, count);
4337d522f475Smrg
4338d522f475Smrg    if (count > screen->sel_atoms_size) {
4339d522f475Smrg	XtFree((char *) atoms);
4340a1f3da82Smrg	atoms = TypeXtMallocN(Atom, count);
4341d522f475Smrg	screen->selection_atoms = atoms;
4342d522f475Smrg	screen->sel_atoms_size = count;
4343d522f475Smrg    }
4344d522f475Smrg    XmuInternStrings(XtDisplay((Widget) xw), selections, count, atoms);
4345d522f475Smrg    for (i = 0; i < count; i++) {
4346d522f475Smrg	int cutbuffer = CutBuffer(atoms[i]);
4347d522f475Smrg	if (cutbuffer >= 0) {
43482eaa94a1Schristos	    unsigned long limit =
43492eaa94a1Schristos	    (unsigned long) (4 * XMaxRequestSize(XtDisplay((Widget) xw)) - 32);
43502eaa94a1Schristos	    if (screen->selection_length > limit) {
435120d2c4d2Smrg		TRACE(("selection too big (%lu bytes), not storing in CUT_BUFFER%d\n",
435220d2c4d2Smrg		       screen->selection_length, cutbuffer));
43530bd37d32Smrg		xtermWarning("selection too big (%lu bytes), not storing in CUT_BUFFER%d\n",
43540bd37d32Smrg			     screen->selection_length, cutbuffer);
4355d522f475Smrg	    } else {
4356d522f475Smrg		/* This used to just use the UTF-8 data, which was totally
4357894e0ac8Smrg		 * broken as not even the corresponding paste code in xterm
4358d522f475Smrg		 * understood this!  So now it converts to Latin1 first.
4359d522f475Smrg		 *   Robert Brady, 2000-09-05
4360d522f475Smrg		 */
4361d522f475Smrg		unsigned long length = screen->selection_length;
4362d522f475Smrg		Char *data = screen->selection_data;
4363d522f475Smrg		if_OPT_WIDE_CHARS((screen), {
4364956cc18dSsnj		    data = UTF8toLatin1(screen, data, length, &length);
4365d522f475Smrg		});
4366d522f475Smrg		TRACE(("XStoreBuffer(%d)\n", cutbuffer));
4367d522f475Smrg		XStoreBuffer(XtDisplay((Widget) xw),
4368d522f475Smrg			     (char *) data,
4369d522f475Smrg			     (int) length,
4370d522f475Smrg			     cutbuffer);
4371d522f475Smrg	    }
43722e4f8982Smrg	} else if (keepClipboard(atoms[i])) {
43732e4f8982Smrg	    Char *buf;
43742e4f8982Smrg	    TRACE(("saving selection to clipboard buffer\n"));
43752e4f8982Smrg	    if ((buf = (Char *) malloc((size_t) screen->selection_length))
43762e4f8982Smrg		== 0)
43772e4f8982Smrg		SysError(ERROR_BMALLOC2);
43782e4f8982Smrg
43792e4f8982Smrg	    XtFree((char *) screen->clipboard_data);
43802e4f8982Smrg	    memcpy(buf, screen->selection_data, screen->selection_length);
43812e4f8982Smrg	    screen->clipboard_data = buf;
43822e4f8982Smrg	    screen->clipboard_size = screen->selection_length;
438301037d57Smrg	} else if (screen->selection_length == 0) {
438401037d57Smrg	    XtDisownSelection((Widget) xw, atoms[i], screen->selection_time);
4385d522f475Smrg	} else if (!screen->replyToEmacs) {
4386d522f475Smrg	    have_selection |=
4387d522f475Smrg		XtOwnSelection((Widget) xw, atoms[i],
4388d522f475Smrg			       screen->selection_time,
4389d522f475Smrg			       ConvertSelection, LoseSelection, SelectionDone);
4390d522f475Smrg	}
4391d522f475Smrg    }
4392d522f475Smrg    if (!screen->replyToEmacs)
4393d522f475Smrg	screen->selection_count = count;
4394d522f475Smrg    if (!have_selection)
4395d522f475Smrg	TrackText(xw, &zeroCELL, &zeroCELL);
4396d522f475Smrg}
4397d522f475Smrg
4398d522f475Smrgstatic void
4399e0a2b6dfSmrgResetSelectionState(TScreen *screen)
4400d522f475Smrg{
4401d522f475Smrg    screen->selection_count = 0;
4402d522f475Smrg    screen->startH = zeroCELL;
4403d522f475Smrg    screen->endH = zeroCELL;
4404d522f475Smrg}
4405d522f475Smrg
4406d522f475Smrgvoid
4407d522f475SmrgDisownSelection(XtermWidget xw)
4408d522f475Smrg{
4409956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4410d522f475Smrg    Atom *atoms = screen->selection_atoms;
4411d522f475Smrg    Cardinal count = screen->selection_count;
4412d522f475Smrg    Cardinal i;
4413d522f475Smrg
4414d522f475Smrg    TRACE(("DisownSelection count %d, start %d.%d, end %d.%d\n",
4415d522f475Smrg	   count,
4416d522f475Smrg	   screen->startH.row,
4417d522f475Smrg	   screen->startH.col,
4418d522f475Smrg	   screen->endH.row,
4419d522f475Smrg	   screen->endH.col));
4420d522f475Smrg
4421d522f475Smrg    for (i = 0; i < count; i++) {
4422d522f475Smrg	int cutbuffer = CutBuffer(atoms[i]);
4423d522f475Smrg	if (cutbuffer < 0) {
4424d522f475Smrg	    XtDisownSelection((Widget) xw, atoms[i],
4425d522f475Smrg			      screen->selection_time);
4426d522f475Smrg	}
4427d522f475Smrg    }
4428d522f475Smrg    /*
4429d522f475Smrg     * If none of the callbacks via XtDisownSelection() reset highlighting
4430d522f475Smrg     * do it now.
4431d522f475Smrg     */
4432d522f475Smrg    if (ScrnHaveSelection(screen)) {
4433d522f475Smrg	/* save data which will be reset */
4434d522f475Smrg	CELL first = screen->startH;
4435d522f475Smrg	CELL last = screen->endH;
4436d522f475Smrg
4437d522f475Smrg	ResetSelectionState(screen);
4438d522f475Smrg	ReHiliteText(xw, &first, &last);
4439d522f475Smrg    } else {
4440d522f475Smrg	ResetSelectionState(screen);
4441d522f475Smrg    }
4442d522f475Smrg}
4443d522f475Smrg
4444d522f475Smrgvoid
4445d522f475SmrgUnhiliteSelection(XtermWidget xw)
4446d522f475Smrg{
4447956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4448d522f475Smrg
4449d522f475Smrg    if (ScrnHaveSelection(screen)) {
4450d522f475Smrg	CELL first = screen->startH;
4451d522f475Smrg	CELL last = screen->endH;
4452d522f475Smrg
4453d522f475Smrg	screen->startH = zeroCELL;
4454d522f475Smrg	screen->endH = zeroCELL;
4455d522f475Smrg	ReHiliteText(xw, &first, &last);
4456d522f475Smrg    }
4457d522f475Smrg}
4458d522f475Smrg
4459d522f475Smrg/* returns number of chars in line from scol to ecol out */
4460d522f475Smrg/* ARGSUSED */
4461d522f475Smrgstatic int
4462e0a2b6dfSmrgLength(TScreen *screen,
4463d522f475Smrg       int row,
4464d522f475Smrg       int scol,
4465d522f475Smrg       int ecol)
4466d522f475Smrg{
446701037d57Smrg    CLineData *ld = GET_LINEDATA(screen, row);
446801037d57Smrg    const int lastcol = LastTextCol(screen, ld, row);
4469d522f475Smrg
4470d522f475Smrg    if (ecol > lastcol)
4471d522f475Smrg	ecol = lastcol;
4472d522f475Smrg    return (ecol - scol + 1);
4473d522f475Smrg}
4474d522f475Smrg
4475d522f475Smrg/* copies text into line, preallocated */
4476d522f475Smrgstatic Char *
4477e0a2b6dfSmrgSaveText(TScreen *screen,
4478d522f475Smrg	 int row,
4479d522f475Smrg	 int scol,
4480d522f475Smrg	 int ecol,
4481e0a2b6dfSmrg	 Char *lp,		/* pointer to where to put the text */
4482d522f475Smrg	 int *eol)
4483d522f475Smrg{
4484956cc18dSsnj    LineData *ld;
4485d522f475Smrg    int i = 0;
4486d522f475Smrg    Char *result = lp;
4487d522f475Smrg#if OPT_WIDE_CHARS
44882eaa94a1Schristos    unsigned previous = 0;
4489d522f475Smrg#endif
4490d522f475Smrg
4491956cc18dSsnj    ld = GET_LINEDATA(screen, row);
4492d522f475Smrg    i = Length(screen, row, scol, ecol);
4493d522f475Smrg    ecol = scol + i;
4494d522f475Smrg#if OPT_DEC_CHRSET
4495956cc18dSsnj    if (CSET_DOUBLE(GetLineDblCS(ld))) {
4496d522f475Smrg	scol = (scol + 0) / 2;
4497d522f475Smrg	ecol = (ecol + 1) / 2;
4498d522f475Smrg    }
4499d522f475Smrg#endif
4500956cc18dSsnj    *eol = !LineTstWrapped(ld);
4501d522f475Smrg    for (i = scol; i < ecol; i++) {
45022e4f8982Smrg	unsigned c;
45030bd37d32Smrg	assert(i < (int) ld->lineSize);
4504956cc18dSsnj	c = E2A(ld->charData[i]);
4505d522f475Smrg#if OPT_WIDE_CHARS
4506d522f475Smrg	/* We want to strip out every occurrence of HIDDEN_CHAR AFTER a
4507d522f475Smrg	 * wide character.
4508d522f475Smrg	 */
4509894e0ac8Smrg	if (c == HIDDEN_CHAR) {
4510894e0ac8Smrg	    if (isWide((int) previous)) {
4511894e0ac8Smrg		previous = c;
4512894e0ac8Smrg		/* Combining characters attached to double-width characters
4513894e0ac8Smrg		   are in memory attached to the HIDDEN_CHAR */
4514894e0ac8Smrg		if_OPT_WIDE_CHARS(screen, {
4515894e0ac8Smrg		    if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
4516894e0ac8Smrg			size_t off;
4517894e0ac8Smrg			for_each_combData(off, ld) {
45182e4f8982Smrg			    unsigned ch = ld->combData[off][i];
4519894e0ac8Smrg			    if (ch == 0)
4520894e0ac8Smrg				break;
4521894e0ac8Smrg			    lp = convertToUTF8(lp, ch);
4522894e0ac8Smrg			}
4523d522f475Smrg		    }
4524894e0ac8Smrg		});
4525894e0ac8Smrg		continue;
4526894e0ac8Smrg	    } else {
4527894e0ac8Smrg		c = ' ';	/* should not happen, but just in case... */
4528894e0ac8Smrg	    }
4529d522f475Smrg	}
4530d522f475Smrg	previous = c;
4531e0a2b6dfSmrg	if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
4532d522f475Smrg	    lp = convertToUTF8(lp, (c != 0) ? c : ' ');
4533d522f475Smrg	    if_OPT_WIDE_CHARS(screen, {
4534956cc18dSsnj		size_t off;
4535956cc18dSsnj		for_each_combData(off, ld) {
45362e4f8982Smrg		    unsigned ch = ld->combData[off][i];
4537956cc18dSsnj		    if (ch == 0)
4538d522f475Smrg			break;
4539d522f475Smrg		    lp = convertToUTF8(lp, ch);
4540d522f475Smrg		}
4541d522f475Smrg	    });
4542d522f475Smrg	} else
4543d522f475Smrg#endif
4544d522f475Smrg	{
4545d522f475Smrg	    if (c == 0) {
4546d522f475Smrg		c = E2A(' ');
4547d522f475Smrg	    } else if (c < E2A(' ')) {
4548d522f475Smrg		c = DECtoASCII(c);
4549d522f475Smrg	    } else if (c == 0x7f) {
4550d522f475Smrg		c = 0x5f;
4551d522f475Smrg	    }
45522eaa94a1Schristos	    *lp++ = CharOf(A2E(c));
4553d522f475Smrg	}
4554d522f475Smrg	if (c != E2A(' '))
4555d522f475Smrg	    result = lp;
4556d522f475Smrg    }
4557d522f475Smrg
4558d522f475Smrg    /*
4559d522f475Smrg     * If requested, trim trailing blanks from selected lines.  Do not do this
4560d522f475Smrg     * if the line is wrapped.
4561d522f475Smrg     */
4562d522f475Smrg    if (!*eol || !screen->trim_selection)
4563d522f475Smrg	result = lp;
4564d522f475Smrg
4565d522f475Smrg    return (result);
4566d522f475Smrg}
4567d522f475Smrg
4568d522f475Smrg/* 32 + following 7-bit word:
4569d522f475Smrg
4570d522f475Smrg   1:0  Button no: 0, 1, 2.  3=release.
4571d522f475Smrg     2  shift
4572d522f475Smrg     3  meta
4573d522f475Smrg     4  ctrl
4574d522f475Smrg     5  set for motion notify
4575d522f475Smrg     6  set for wheel
4576d522f475Smrg*/
4577d522f475Smrg
4578d522f475Smrg/* Position: 32 - 255. */
4579a1f3da82Smrgstatic int
4580894e0ac8SmrgBtnCode(XButtonEvent *event, int button)
4581d522f475Smrg{
45822eaa94a1Schristos    int result = (int) (32 + (KeyState(event->state) << 2));
4583d522f475Smrg
45840bd37d32Smrg    if (event->type == MotionNotify)
45850bd37d32Smrg	result += 32;
45860bd37d32Smrg
4587d522f475Smrg    if (button < 0 || button > 5) {
4588d522f475Smrg	result += 3;
4589d522f475Smrg    } else {
4590d522f475Smrg	if (button > 3)
4591d522f475Smrg	    result += (64 - 4);
4592d522f475Smrg	result += button;
4593d522f475Smrg    }
45940bd37d32Smrg    TRACE(("BtnCode button %d, %s state " FMT_MODIFIER_NAMES " ->%#x\n",
45950bd37d32Smrg	   button,
45960bd37d32Smrg	   visibleEventType(event->type),
45970bd37d32Smrg	   ARG_MODIFIER_NAMES(event->state),
45980bd37d32Smrg	   result));
4599a1f3da82Smrg    return result;
4600a1f3da82Smrg}
4601a1f3da82Smrg
4602a1f3da82Smrgstatic unsigned
4603913cc679SmrgEmitButtonCode(XtermWidget xw,
4604e0a2b6dfSmrg	       Char *line,
46050bd37d32Smrg	       unsigned count,
4606894e0ac8Smrg	       XButtonEvent *event,
46070bd37d32Smrg	       int button)
4608a1f3da82Smrg{
4609913cc679Smrg    TScreen *screen = TScreenOf(xw);
46100bd37d32Smrg    int value;
4611a1f3da82Smrg
4612913cc679Smrg    if (okSendMousePos(xw) == X10_MOUSE) {
46130bd37d32Smrg	value = CharOf(' ' + button);
4614a1f3da82Smrg    } else {
46150bd37d32Smrg	value = BtnCode(event, button);
46160bd37d32Smrg    }
46170bd37d32Smrg
46180bd37d32Smrg    switch (screen->extend_coords) {
46190bd37d32Smrg    default:
46200bd37d32Smrg	line[count++] = CharOf(value);
46210bd37d32Smrg	break;
46220bd37d32Smrg    case SET_SGR_EXT_MODE_MOUSE:
46230bd37d32Smrg	value -= 32;		/* encoding starts at zero */
46240bd37d32Smrg	/* FALLTHRU */
46250bd37d32Smrg    case SET_URXVT_EXT_MODE_MOUSE:
46260bd37d32Smrg	count += (unsigned) sprintf((char *) line + count, "%d", value);
46270bd37d32Smrg	break;
46280bd37d32Smrg    case SET_EXT_MODE_MOUSE:
46290bd37d32Smrg	if (value < 128) {
46300bd37d32Smrg	    line[count++] = CharOf(value);
46310bd37d32Smrg	} else {
46320bd37d32Smrg	    line[count++] = CharOf(0xC0 + (value >> 6));
46330bd37d32Smrg	    line[count++] = CharOf(0x80 + (value & 0x3F));
46340bd37d32Smrg	}
46350bd37d32Smrg	break;
4636a1f3da82Smrg    }
4637a1f3da82Smrg    return count;
4638d522f475Smrg}
4639d522f475Smrg
46400bd37d32Smrgstatic int
46410bd37d32SmrgFirstBitN(int bits)
46420bd37d32Smrg{
46430bd37d32Smrg    int result = -1;
46440bd37d32Smrg    if (bits > 0) {
46450bd37d32Smrg	result = 0;
46460bd37d32Smrg	while (!(bits & 1)) {
46470bd37d32Smrg	    bits /= 2;
46480bd37d32Smrg	    ++result;
46490bd37d32Smrg	}
46500bd37d32Smrg    }
46510bd37d32Smrg    return result;
46520bd37d32Smrg}
46530bd37d32Smrg
46540bd37d32Smrg#define ButtonBit(button) ((button >= 0) ? (1 << (button)) : 0)
46550bd37d32Smrg
4656913cc679Smrg#define EMIT_BUTTON(button) EmitButtonCode(xw, line, count, event, button)
46570bd37d32Smrg
4658d522f475Smrgstatic void
4659894e0ac8SmrgEditorButton(XtermWidget xw, XButtonEvent *event)
4660d522f475Smrg{
4661956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4662d522f475Smrg    int pty = screen->respond;
46630bd37d32Smrg    int mouse_limit = MouseLimit(screen);
46640bd37d32Smrg    Char line[32];
46650bd37d32Smrg    Char final = 'M';
4666d522f475Smrg    int row, col;
4667d522f475Smrg    int button;
4668d522f475Smrg    unsigned count = 0;
4669d522f475Smrg    Boolean changed = True;
4670d522f475Smrg
4671d522f475Smrg    /* If button event, get button # adjusted for DEC compatibility */
46722eaa94a1Schristos    button = (int) (event->button - 1);
4673d522f475Smrg    if (button >= 3)
4674d522f475Smrg	button++;
4675d522f475Smrg
4676d522f475Smrg    /* Compute character position of mouse pointer */
4677d522f475Smrg    row = (event->y - screen->border) / FontHeight(screen);
4678d522f475Smrg    col = (event->x - OriginX(screen)) / FontWidth(screen);
4679d522f475Smrg
4680d522f475Smrg    /* Limit to screen dimensions */
4681d522f475Smrg    if (row < 0)
4682d522f475Smrg	row = 0;
4683d522f475Smrg    else if (row > screen->max_row)
4684d522f475Smrg	row = screen->max_row;
4685d522f475Smrg
4686d522f475Smrg    if (col < 0)
4687d522f475Smrg	col = 0;
4688d522f475Smrg    else if (col > screen->max_col)
4689d522f475Smrg	col = screen->max_col;
4690492d43a5Smrg
46910bd37d32Smrg    if (mouse_limit > 0) {
46920bd37d32Smrg	/* Limit to representable mouse dimensions */
46930bd37d32Smrg	if (row > mouse_limit)
46940bd37d32Smrg	    row = mouse_limit;
46950bd37d32Smrg	if (col > mouse_limit)
46960bd37d32Smrg	    col = mouse_limit;
46970bd37d32Smrg    }
4698d522f475Smrg
4699d522f475Smrg    /* Build key sequence starting with \E[M */
4700d522f475Smrg    if (screen->control_eight_bits) {
4701d522f475Smrg	line[count++] = ANSI_CSI;
4702d522f475Smrg    } else {
4703d522f475Smrg	line[count++] = ANSI_ESC;
4704d522f475Smrg	line[count++] = '[';
4705d522f475Smrg    }
47060bd37d32Smrg    switch (screen->extend_coords) {
47070bd37d32Smrg    case 0:
47080bd37d32Smrg    case SET_EXT_MODE_MOUSE:
4709d522f475Smrg#if OPT_SCO_FUNC_KEYS
47100bd37d32Smrg	if (xw->keyboard.type == keyboardIsSCO) {
47110bd37d32Smrg	    /*
47120bd37d32Smrg	     * SCO function key F1 is \E[M, which would conflict with xterm's
47130bd37d32Smrg	     * normal kmous.
47140bd37d32Smrg	     */
47150bd37d32Smrg	    line[count++] = '>';
47160bd37d32Smrg	}
4717d522f475Smrg#endif
47180bd37d32Smrg	line[count++] = final;
47190bd37d32Smrg	break;
47200bd37d32Smrg    case SET_SGR_EXT_MODE_MOUSE:
47210bd37d32Smrg	line[count++] = '<';
47220bd37d32Smrg	break;
47230bd37d32Smrg    }
4724d522f475Smrg
4725d522f475Smrg    /* Add event code to key sequence */
4726913cc679Smrg    if (okSendMousePos(xw) == X10_MOUSE) {
47270bd37d32Smrg	count = EMIT_BUTTON(button);
4728d522f475Smrg    } else {
4729d522f475Smrg	/* Button-Motion events */
4730d522f475Smrg	switch (event->type) {
4731d522f475Smrg	case ButtonPress:
47320bd37d32Smrg	    screen->mouse_button |= ButtonBit(button);
47330bd37d32Smrg	    count = EMIT_BUTTON(button);
4734d522f475Smrg	    break;
4735d522f475Smrg	case ButtonRelease:
4736d522f475Smrg	    /*
4737d522f475Smrg	     * Wheel mouse interface generates release-events for buttons
4738d522f475Smrg	     * 4 and 5, coded here as 3 and 4 respectively.  We change the
47390bd37d32Smrg	     * release for buttons 1..3 to a -1, which will be later mapped
47400bd37d32Smrg	     * into a "0" (some button was released).
4741d522f475Smrg	     */
47420bd37d32Smrg	    screen->mouse_button &= ~ButtonBit(button);
47430bd37d32Smrg	    if (button < 3) {
47440bd37d32Smrg		switch (screen->extend_coords) {
47450bd37d32Smrg		case SET_SGR_EXT_MODE_MOUSE:
47460bd37d32Smrg		    final = 'm';
47470bd37d32Smrg		    break;
47480bd37d32Smrg		default:
47490bd37d32Smrg		    button = -1;
47500bd37d32Smrg		    break;
47510bd37d32Smrg		}
47520bd37d32Smrg	    }
47530bd37d32Smrg	    count = EMIT_BUTTON(button);
4754d522f475Smrg	    break;
4755d522f475Smrg	case MotionNotify:
4756d522f475Smrg	    /* BTN_EVENT_MOUSE and ANY_EVENT_MOUSE modes send motion
4757d522f475Smrg	     * events only if character cell has changed.
4758d522f475Smrg	     */
4759d522f475Smrg	    if ((row == screen->mouse_row)
4760d522f475Smrg		&& (col == screen->mouse_col)) {
4761d522f475Smrg		changed = False;
4762d522f475Smrg	    } else {
47630bd37d32Smrg		count = EMIT_BUTTON(FirstBitN(screen->mouse_button));
4764d522f475Smrg	    }
4765d522f475Smrg	    break;
4766d522f475Smrg	default:
4767d522f475Smrg	    changed = False;
4768d522f475Smrg	    break;
4769d522f475Smrg	}
4770d522f475Smrg    }
4771d522f475Smrg
4772d522f475Smrg    if (changed) {
4773d522f475Smrg	screen->mouse_row = row;
4774d522f475Smrg	screen->mouse_col = col;
4775d522f475Smrg
4776492d43a5Smrg	TRACE(("mouse at %d,%d button+mask = %#x\n", row, col, line[count - 1]));
4777d522f475Smrg
4778492d43a5Smrg	/* Add pointer position to key sequence */
47790bd37d32Smrg	count = EmitMousePositionSeparator(screen, line, count);
4780492d43a5Smrg	count = EmitMousePosition(screen, line, count, col);
47810bd37d32Smrg	count = EmitMousePositionSeparator(screen, line, count);
4782492d43a5Smrg	count = EmitMousePosition(screen, line, count, row);
4783d522f475Smrg
47840bd37d32Smrg	switch (screen->extend_coords) {
47850bd37d32Smrg	case SET_SGR_EXT_MODE_MOUSE:
47860bd37d32Smrg	case SET_URXVT_EXT_MODE_MOUSE:
47870bd37d32Smrg	    line[count++] = final;
47880bd37d32Smrg	    break;
47890bd37d32Smrg	}
47900bd37d32Smrg
4791d522f475Smrg	/* Transmit key sequence to process running under xterm */
4792d522f475Smrg	v_write(pty, line, count);
4793d522f475Smrg    }
4794d522f475Smrg    return;
4795d522f475Smrg}
4796d522f475Smrg
4797913cc679Smrg/*
4798913cc679Smrg * Check the current send_mouse_pos against allowed mouse-operations, returning
4799913cc679Smrg * none if it is disallowed.
4800913cc679Smrg */
4801913cc679SmrgXtermMouseModes
4802913cc679SmrgokSendMousePos(XtermWidget xw)
4803913cc679Smrg{
4804913cc679Smrg    TScreen *screen = TScreenOf(xw);
4805913cc679Smrg    XtermMouseModes result = screen->send_mouse_pos;
4806913cc679Smrg
4807913cc679Smrg    switch (result) {
4808913cc679Smrg    case MOUSE_OFF:
4809913cc679Smrg	break;
4810913cc679Smrg    case X10_MOUSE:
4811913cc679Smrg	if (!AllowMouseOps(xw, emX10))
4812913cc679Smrg	    result = MOUSE_OFF;
4813913cc679Smrg	break;
4814913cc679Smrg    case VT200_MOUSE:
4815913cc679Smrg	if (!AllowMouseOps(xw, emVT200Click))
4816913cc679Smrg	    result = MOUSE_OFF;
4817913cc679Smrg	break;
4818913cc679Smrg    case VT200_HIGHLIGHT_MOUSE:
4819913cc679Smrg	if (!AllowMouseOps(xw, emVT200Hilite))
4820913cc679Smrg	    result = MOUSE_OFF;
4821913cc679Smrg	break;
4822913cc679Smrg    case BTN_EVENT_MOUSE:
4823913cc679Smrg	if (!AllowMouseOps(xw, emAnyButton))
4824913cc679Smrg	    result = MOUSE_OFF;
4825913cc679Smrg	break;
4826913cc679Smrg    case ANY_EVENT_MOUSE:
4827913cc679Smrg	if (!AllowMouseOps(xw, emAnyEvent))
4828913cc679Smrg	    result = MOUSE_OFF;
4829913cc679Smrg	break;
4830913cc679Smrg    case DEC_LOCATOR:
4831913cc679Smrg	if (!AllowMouseOps(xw, emLocator))
4832913cc679Smrg	    result = MOUSE_OFF;
4833913cc679Smrg	break;
4834913cc679Smrg    }
4835913cc679Smrg    return result;
4836913cc679Smrg}
4837913cc679Smrg
4838d522f475Smrg#if OPT_FOCUS_EVENT
4839913cc679Smrg/*
4840913cc679Smrg * Check the current send_focus_pos against allowed mouse-operations, returning
4841913cc679Smrg * none if it is disallowed.
4842913cc679Smrg */
4843913cc679Smrgstatic int
4844913cc679SmrgokSendFocusPos(XtermWidget xw)
4845d522f475Smrg{
4846956cc18dSsnj    TScreen *screen = TScreenOf(xw);
4847913cc679Smrg    int result = screen->send_focus_pos;
4848913cc679Smrg
4849913cc679Smrg    if (!AllowMouseOps(xw, emFocusEvent)) {
4850913cc679Smrg	result = False;
4851913cc679Smrg    }
4852913cc679Smrg    return result;
4853913cc679Smrg}
4854d522f475Smrg
4855913cc679Smrgvoid
4856913cc679SmrgSendFocusButton(XtermWidget xw, XFocusChangeEvent *event)
4857913cc679Smrg{
4858913cc679Smrg    if (okSendFocusPos(xw)) {
4859d522f475Smrg	ANSI reply;
4860d522f475Smrg
4861d522f475Smrg	memset(&reply, 0, sizeof(reply));
4862d522f475Smrg	reply.a_type = ANSI_CSI;
4863d522f475Smrg
4864d522f475Smrg#if OPT_SCO_FUNC_KEYS
4865d522f475Smrg	if (xw->keyboard.type == keyboardIsSCO) {
4866d522f475Smrg	    reply.a_pintro = '>';
4867d522f475Smrg	}
4868d522f475Smrg#endif
48692eaa94a1Schristos	reply.a_final = CharOf((event->type == FocusIn) ? 'I' : 'O');
4870d522f475Smrg	unparseseq(xw, &reply);
4871d522f475Smrg    }
4872d522f475Smrg    return;
4873d522f475Smrg}
4874d522f475Smrg#endif /* OPT_FOCUS_EVENT */
48750bd37d32Smrg
48760bd37d32Smrg#if OPT_SELECTION_OPS
48770bd37d32Smrg/*
48780bd37d32Smrg * Get the event-time, needed to process selections.
48790bd37d32Smrg */
48800bd37d32Smrgstatic Time
4881894e0ac8SmrggetEventTime(XEvent *event)
48820bd37d32Smrg{
48830bd37d32Smrg    Time result;
48840bd37d32Smrg
48850bd37d32Smrg    if (IsBtnEvent(event)) {
48860bd37d32Smrg	result = ((XButtonEvent *) event)->time;
48870bd37d32Smrg    } else if (IsKeyEvent(event)) {
48880bd37d32Smrg	result = ((XKeyEvent *) event)->time;
48890bd37d32Smrg    } else {
48900bd37d32Smrg	result = 0;
48910bd37d32Smrg    }
48920bd37d32Smrg
48930bd37d32Smrg    return result;
48940bd37d32Smrg}
48950bd37d32Smrg
48960bd37d32Smrg/* obtain the selection string, passing the endpoints to caller's parameters */
489701037d57Smrgstatic void
489801037d57SmrgdoSelectionFormat(XtermWidget xw,
489901037d57Smrg		  Widget w,
490001037d57Smrg		  XEvent *event,
490101037d57Smrg		  String *params,
490201037d57Smrg		  Cardinal *num_params,
490301037d57Smrg		  FormatSelect format_select)
49040bd37d32Smrg{
49050bd37d32Smrg    TScreen *screen = TScreenOf(xw);
490601037d57Smrg    InternalSelect *mydata = &(screen->internal_select);
490701037d57Smrg
490801037d57Smrg    memset(mydata, 0, sizeof(*mydata));
490901037d57Smrg    mydata->format = x_strdup(params[0]);
491001037d57Smrg    mydata->format_select = format_select;
49110bd37d32Smrg
49120bd37d32Smrg    /* override flags so that SelectionReceived only updates a buffer */
49130bd37d32Smrg#if OPT_PASTE64
491401037d57Smrg    mydata->base64_paste = screen->base64_paste;
49150bd37d32Smrg    screen->base64_paste = 0;
49160bd37d32Smrg#endif
49170bd37d32Smrg#if OPT_READLINE
491801037d57Smrg    mydata->paste_brackets = screen->paste_brackets;
49190bd37d32Smrg    SCREEN_FLAG_unset(screen, paste_brackets);
49200bd37d32Smrg#endif
49210bd37d32Smrg
49220bd37d32Smrg    screen->selectToBuffer = True;
49230bd37d32Smrg    xtermGetSelection(w, getEventTime(event), params + 1, *num_params - 1, NULL);
49240bd37d32Smrg}
49250bd37d32Smrg
49260bd37d32Smrg/* obtain data from the screen, passing the endpoints to caller's parameters */
49270bd37d32Smrgstatic char *
4928913cc679SmrggetDataFromScreen(XtermWidget xw, XEvent *event, String method, CELL *start, CELL *finish)
49290bd37d32Smrg{
49300bd37d32Smrg    TScreen *screen = TScreenOf(xw);
49310bd37d32Smrg
49320bd37d32Smrg    CELL save_old_start = screen->startH;
49330bd37d32Smrg    CELL save_old_end = screen->endH;
49340bd37d32Smrg
49350bd37d32Smrg    CELL save_startSel = screen->startSel;
49360bd37d32Smrg    CELL save_startRaw = screen->startRaw;
49370bd37d32Smrg    CELL save_finishSel = screen->endSel;
49380bd37d32Smrg    CELL save_finishRaw = screen->endRaw;
49390bd37d32Smrg
49400bd37d32Smrg    int save_firstValidRow = screen->firstValidRow;
49410bd37d32Smrg    int save_lastValidRow = screen->lastValidRow;
49420bd37d32Smrg
494301037d57Smrg    const Cardinal noClick = 0;
494401037d57Smrg    int save_numberOfClicks = screen->numberOfClicks;
494501037d57Smrg
49460bd37d32Smrg    SelectUnit saveUnits = screen->selectUnit;
494701037d57Smrg    SelectUnit saveMap = screen->selectMap[noClick];
49480bd37d32Smrg#if OPT_SELECT_REGEX
494901037d57Smrg    char *saveExpr = screen->selectExpr[noClick];
49500bd37d32Smrg#endif
49510bd37d32Smrg
49520bd37d32Smrg    Char *save_selection_data = screen->selection_data;
49530bd37d32Smrg    int save_selection_size = screen->selection_size;
49540bd37d32Smrg    unsigned long save_selection_length = screen->selection_length;
49550bd37d32Smrg
49560bd37d32Smrg    char *result = 0;
49570bd37d32Smrg
49580bd37d32Smrg    TRACE(("getDataFromScreen %s\n", method));
49590bd37d32Smrg
49600bd37d32Smrg    screen->selection_data = 0;
49610bd37d32Smrg    screen->selection_size = 0;
49620bd37d32Smrg    screen->selection_length = 0;
49630bd37d32Smrg
496401037d57Smrg    screen->numberOfClicks = 1;
496501037d57Smrg    lookupSelectUnit(xw, noClick, method);
496601037d57Smrg    screen->selectUnit = screen->selectMap[noClick];
49670bd37d32Smrg
49680bd37d32Smrg    memset(start, 0, sizeof(*start));
4969913cc679Smrg    if (IsBtnEvent(event)) {
4970913cc679Smrg	XButtonEvent *btn_event = (XButtonEvent *) event;
4971913cc679Smrg	CELL cell;
4972913cc679Smrg	screen->firstValidRow = 0;
4973913cc679Smrg	screen->lastValidRow = screen->max_row;
4974913cc679Smrg	PointToCELL(screen, btn_event->y, btn_event->x, &cell);
4975913cc679Smrg	start->row = cell.row;
4976913cc679Smrg	start->col = cell.col;
4977913cc679Smrg	finish->row = cell.row;
4978913cc679Smrg	finish->col = screen->max_col;
4979913cc679Smrg    } else {
4980913cc679Smrg	start->row = screen->cur_row;
4981913cc679Smrg	start->col = screen->cur_col;
4982913cc679Smrg	finish->row = screen->cur_row;
4983913cc679Smrg	finish->col = screen->max_col;
4984913cc679Smrg    }
49850bd37d32Smrg
49860bd37d32Smrg    ComputeSelect(xw, start, finish, False);
49870bd37d32Smrg    SaltTextAway(xw, &(screen->startSel), &(screen->endSel));
49880bd37d32Smrg
49890bd37d32Smrg    if (screen->selection_length && screen->selection_data) {
49900bd37d32Smrg	TRACE(("...getDataFromScreen selection_data %.*s\n",
49910bd37d32Smrg	       (int) screen->selection_length,
49920bd37d32Smrg	       screen->selection_data));
49930bd37d32Smrg	result = malloc(screen->selection_length + 1);
49940bd37d32Smrg	if (result) {
49950bd37d32Smrg	    memcpy(result, screen->selection_data, screen->selection_length);
49960bd37d32Smrg	    result[screen->selection_length] = 0;
49970bd37d32Smrg	}
49980bd37d32Smrg	free(screen->selection_data);
49990bd37d32Smrg    }
50000bd37d32Smrg
50010bd37d32Smrg    TRACE(("...getDataFromScreen restoring previous selection\n"));
50020bd37d32Smrg
50030bd37d32Smrg    screen->startSel = save_startSel;
50040bd37d32Smrg    screen->startRaw = save_startRaw;
50050bd37d32Smrg    screen->endSel = save_finishSel;
50060bd37d32Smrg    screen->endRaw = save_finishRaw;
50070bd37d32Smrg
50080bd37d32Smrg    screen->firstValidRow = save_firstValidRow;
50090bd37d32Smrg    screen->lastValidRow = save_lastValidRow;
50100bd37d32Smrg
501101037d57Smrg    screen->numberOfClicks = save_numberOfClicks;
50120bd37d32Smrg    screen->selectUnit = saveUnits;
501301037d57Smrg    screen->selectMap[noClick] = saveMap;
50140bd37d32Smrg#if OPT_SELECT_REGEX
501501037d57Smrg    screen->selectExpr[noClick] = saveExpr;
50160bd37d32Smrg#endif
50170bd37d32Smrg
50180bd37d32Smrg    screen->selection_data = save_selection_data;
50190bd37d32Smrg    screen->selection_size = save_selection_size;
50200bd37d32Smrg    screen->selection_length = save_selection_length;
50210bd37d32Smrg
50220bd37d32Smrg    TrackText(xw, &save_old_start, &save_old_end);
50230bd37d32Smrg
50240bd37d32Smrg    TRACE(("...getDataFromScreen done\n"));
50250bd37d32Smrg    return result;
50260bd37d32Smrg}
50270bd37d32Smrg
50280bd37d32Smrg/*
50290bd37d32Smrg * Split-up the format before substituting data, to avoid quoting issues.
50300bd37d32Smrg * The resource mechanism has a limited ability to handle escapes.  We take
50310bd37d32Smrg * the result as if it were an sh-type string and parse it into a regular
50320bd37d32Smrg * argv array.
50330bd37d32Smrg */
50340bd37d32Smrgstatic char **
50350bd37d32SmrgtokenizeFormat(String format)
50360bd37d32Smrg{
50370bd37d32Smrg    char **result = 0;
50380bd37d32Smrg    int argc;
50390bd37d32Smrg
50400bd37d32Smrg    format = x_skip_blanks(format);
50410bd37d32Smrg    if (*format != '\0') {
50420bd37d32Smrg	char *blob = x_strdup(format);
50432e4f8982Smrg	int pass;
50440bd37d32Smrg
50450bd37d32Smrg	for (pass = 0; pass < 2; ++pass) {
50460bd37d32Smrg	    int used = 0;
50470bd37d32Smrg	    int first = 1;
50480bd37d32Smrg	    int escaped = 0;
50490bd37d32Smrg	    int squoted = 0;
50500bd37d32Smrg	    int dquoted = 0;
50512e4f8982Smrg	    int n;
50520bd37d32Smrg
50530bd37d32Smrg	    argc = 0;
50540bd37d32Smrg	    for (n = 0; format[n] != '\0'; ++n) {
50550bd37d32Smrg		if (escaped) {
50560bd37d32Smrg		    blob[used++] = format[n];
50570bd37d32Smrg		    escaped = 0;
50580bd37d32Smrg		} else if (format[n] == '"') {
50590bd37d32Smrg		    if (!squoted) {
50600bd37d32Smrg			if (!dquoted)
50610bd37d32Smrg			    blob[used++] = format[n];
50620bd37d32Smrg			dquoted = !dquoted;
50630bd37d32Smrg		    }
50640bd37d32Smrg		} else if (format[n] == '\'') {
50650bd37d32Smrg		    if (!dquoted) {
50660bd37d32Smrg			if (!squoted)
50670bd37d32Smrg			    blob[used++] = format[n];
50680bd37d32Smrg			squoted = !squoted;
50690bd37d32Smrg		    }
50700bd37d32Smrg		} else if (format[n] == '\\') {
50710bd37d32Smrg		    blob[used++] = format[n];
50720bd37d32Smrg		    escaped = 1;
50730bd37d32Smrg		} else {
50740bd37d32Smrg		    if (first) {
50750bd37d32Smrg			first = 0;
50760bd37d32Smrg			if (pass) {
50770bd37d32Smrg			    result[argc] = &blob[n];
50780bd37d32Smrg			}
50790bd37d32Smrg			++argc;
50800bd37d32Smrg		    }
50810bd37d32Smrg		    if (isspace((Char) format[n])) {
50820bd37d32Smrg			first = !isspace((Char) format[n + 1]);
50830bd37d32Smrg			if (squoted || dquoted) {
50840bd37d32Smrg			    blob[used++] = format[n];
50850bd37d32Smrg			} else if (first) {
50860bd37d32Smrg			    blob[used++] = '\0';
50870bd37d32Smrg			}
50880bd37d32Smrg		    } else {
50890bd37d32Smrg			blob[used++] = format[n];
50900bd37d32Smrg		    }
50910bd37d32Smrg		}
50920bd37d32Smrg	    }
50930bd37d32Smrg	    blob[used] = '\0';
50940bd37d32Smrg	    assert(strlen(blob) <= strlen(format));
50950bd37d32Smrg	    if (!pass) {
50960bd37d32Smrg		result = TypeCallocN(char *, argc + 1);
50970bd37d32Smrg		if (result == 0) {
50980bd37d32Smrg		    free(blob);
50990bd37d32Smrg		    break;
51000bd37d32Smrg		}
51010bd37d32Smrg	    }
51020bd37d32Smrg	}
51030bd37d32Smrg    }
51040bd37d32Smrg#if OPT_TRACE
51050bd37d32Smrg    if (result) {
51060bd37d32Smrg	TRACE(("tokenizeFormat %s\n", format));
51070bd37d32Smrg	for (argc = 0; result[argc]; ++argc) {
51080bd37d32Smrg	    TRACE(("argv[%d] = %s\n", argc, result[argc]));
51090bd37d32Smrg	}
51100bd37d32Smrg    }
51110bd37d32Smrg#endif
51120bd37d32Smrg
51130bd37d32Smrg    return result;
51140bd37d32Smrg}
51150bd37d32Smrg
51160bd37d32Smrgstatic void
5117e0a2b6dfSmrgformatVideoAttrs(XtermWidget xw, char *buffer, CELL *cell)
51180bd37d32Smrg{
51190bd37d32Smrg    TScreen *screen = TScreenOf(xw);
51200bd37d32Smrg    LineData *ld = GET_LINEDATA(screen, cell->row);
51210bd37d32Smrg
51220bd37d32Smrg    *buffer = '\0';
51230bd37d32Smrg    if (ld != 0 && cell->col < (int) ld->lineSize) {
5124894e0ac8Smrg	IAttr attribs = ld->attribs[cell->col];
51250bd37d32Smrg	const char *delim = "";
51260bd37d32Smrg
51270bd37d32Smrg	if (attribs & INVERSE) {
51280bd37d32Smrg	    buffer += sprintf(buffer, "7");
51290bd37d32Smrg	    delim = ";";
51300bd37d32Smrg	}
51310bd37d32Smrg	if (attribs & UNDERLINE) {
51320bd37d32Smrg	    buffer += sprintf(buffer, "%s4", delim);
51330bd37d32Smrg	    delim = ";";
51340bd37d32Smrg	}
51350bd37d32Smrg	if (attribs & BOLD) {
51360bd37d32Smrg	    buffer += sprintf(buffer, "%s1", delim);
51370bd37d32Smrg	    delim = ";";
51380bd37d32Smrg	}
51390bd37d32Smrg	if (attribs & BLINK) {
51400bd37d32Smrg	    buffer += sprintf(buffer, "%s5", delim);
51410bd37d32Smrg	    delim = ";";
51420bd37d32Smrg	}
51430bd37d32Smrg#if OPT_ISO_COLORS
51440bd37d32Smrg	if (attribs & FG_COLOR) {
51450bd37d32Smrg	    unsigned fg = extract_fg(xw, ld->color[cell->col], attribs);
51460bd37d32Smrg	    if (fg < 8) {
51470bd37d32Smrg		fg += 30;
51480bd37d32Smrg	    } else if (fg < 16) {
51490bd37d32Smrg		fg += 90;
51500bd37d32Smrg	    } else {
51510bd37d32Smrg		buffer += sprintf(buffer, "%s38;5", delim);
51520bd37d32Smrg		delim = ";";
51530bd37d32Smrg	    }
51540bd37d32Smrg	    buffer += sprintf(buffer, "%s%u", delim, fg);
51550bd37d32Smrg	    delim = ";";
51560bd37d32Smrg	}
51570bd37d32Smrg	if (attribs & BG_COLOR) {
51580bd37d32Smrg	    unsigned bg = extract_bg(xw, ld->color[cell->col], attribs);
51590bd37d32Smrg	    if (bg < 8) {
51600bd37d32Smrg		bg += 40;
51610bd37d32Smrg	    } else if (bg < 16) {
51620bd37d32Smrg		bg += 100;
51630bd37d32Smrg	    } else {
51640bd37d32Smrg		buffer += sprintf(buffer, "%s48;5", delim);
51650bd37d32Smrg		delim = ";";
51660bd37d32Smrg	    }
51670bd37d32Smrg	    (void) sprintf(buffer, "%s%u", delim, bg);
51680bd37d32Smrg	}
51690bd37d32Smrg#endif
51700bd37d32Smrg    }
51710bd37d32Smrg}
51720bd37d32Smrg
51732e4f8982Smrgstatic char *
51742e4f8982SmrgformatStrlen(char *target, char *source, int freeit)
51752e4f8982Smrg{
51762e4f8982Smrg    if (source != 0) {
51772e4f8982Smrg	sprintf(target, "%u", (unsigned) strlen(source));
51782e4f8982Smrg	if (freeit) {
51792e4f8982Smrg	    free(source);
51802e4f8982Smrg	}
51812e4f8982Smrg    } else {
51822e4f8982Smrg	strcpy(target, "0");
51832e4f8982Smrg    }
51842e4f8982Smrg    return target;
51852e4f8982Smrg}
51862e4f8982Smrg
51870bd37d32Smrg/* substitute data into format, reallocating the result */
51880bd37d32Smrgstatic char *
51890bd37d32SmrgexpandFormat(XtermWidget xw,
51900bd37d32Smrg	     const char *format,
51910bd37d32Smrg	     char *data,
5192e0a2b6dfSmrg	     CELL *start,
5193e0a2b6dfSmrg	     CELL *finish)
51940bd37d32Smrg{
51950bd37d32Smrg    char *result = 0;
51960bd37d32Smrg    if (!IsEmpty(format)) {
51970bd37d32Smrg	static char empty[1];
51980bd37d32Smrg	int pass;
51990bd37d32Smrg	int n;
52000bd37d32Smrg	char numbers[80];
52010bd37d32Smrg
52020bd37d32Smrg	if (data == 0)
52030bd37d32Smrg	    data = empty;
52040bd37d32Smrg
52050bd37d32Smrg	for (pass = 0; pass < 2; ++pass) {
52060bd37d32Smrg	    size_t need = 0;
52070bd37d32Smrg
52080bd37d32Smrg	    for (n = 0; format[n] != '\0'; ++n) {
52090bd37d32Smrg
52100bd37d32Smrg		if (format[n] == '%') {
52112e4f8982Smrg		    char *value = 0;
52122e4f8982Smrg
52130bd37d32Smrg		    switch (format[++n]) {
52140bd37d32Smrg		    case '%':
52150bd37d32Smrg			if (pass) {
52160bd37d32Smrg			    result[need] = format[n];
52170bd37d32Smrg			}
52180bd37d32Smrg			++need;
52190bd37d32Smrg			break;
52200bd37d32Smrg		    case 'P':
52210bd37d32Smrg			sprintf(numbers, "%d;%d",
52220bd37d32Smrg				TScreenOf(xw)->topline + start->row + 1,
52230bd37d32Smrg				start->col + 1);
52240bd37d32Smrg			value = numbers;
52250bd37d32Smrg			break;
52260bd37d32Smrg		    case 'p':
52270bd37d32Smrg			sprintf(numbers, "%d;%d",
52280bd37d32Smrg				TScreenOf(xw)->topline + finish->row + 1,
52290bd37d32Smrg				finish->col + 1);
52300bd37d32Smrg			value = numbers;
52310bd37d32Smrg			break;
52322e4f8982Smrg		    case 'R':
52332e4f8982Smrg			value = formatStrlen(numbers, x_strrtrim(data), 1);
52342e4f8982Smrg			break;
52352e4f8982Smrg		    case 'r':
52362e4f8982Smrg			value = x_strrtrim(data);
52372e4f8982Smrg			break;
52380bd37d32Smrg		    case 'S':
52392e4f8982Smrg			value = formatStrlen(numbers, data, 0);
52400bd37d32Smrg			break;
52410bd37d32Smrg		    case 's':
52420bd37d32Smrg			value = data;
52430bd37d32Smrg			break;
52440bd37d32Smrg		    case 'T':
52452e4f8982Smrg			value = formatStrlen(numbers, x_strtrim(data), 1);
52460bd37d32Smrg			break;
52470bd37d32Smrg		    case 't':
52480bd37d32Smrg			value = x_strtrim(data);
52490bd37d32Smrg			break;
52500bd37d32Smrg		    case 'V':
52510bd37d32Smrg			formatVideoAttrs(xw, numbers, start);
52520bd37d32Smrg			value = numbers;
52530bd37d32Smrg			break;
52540bd37d32Smrg		    case 'v':
52550bd37d32Smrg			formatVideoAttrs(xw, numbers, finish);
52560bd37d32Smrg			value = numbers;
52570bd37d32Smrg			break;
52580bd37d32Smrg		    default:
52590bd37d32Smrg			if (pass) {
52600bd37d32Smrg			    result[need] = format[n];
52610bd37d32Smrg			}
52620bd37d32Smrg			--n;
52630bd37d32Smrg			++need;
52640bd37d32Smrg			break;
52650bd37d32Smrg		    }
52660bd37d32Smrg		    if (value != 0) {
52670bd37d32Smrg			if (pass) {
52680bd37d32Smrg			    strcpy(result + need, value);
52690bd37d32Smrg			}
52700bd37d32Smrg			need += strlen(value);
52710bd37d32Smrg			if (value != numbers && value != data) {
52720bd37d32Smrg			    free(value);
52730bd37d32Smrg			}
52740bd37d32Smrg		    }
52750bd37d32Smrg		} else {
52760bd37d32Smrg		    if (pass) {
52770bd37d32Smrg			result[need] = format[n];
52780bd37d32Smrg		    }
52790bd37d32Smrg		    ++need;
52800bd37d32Smrg		}
52810bd37d32Smrg	    }
52820bd37d32Smrg	    if (pass) {
52830bd37d32Smrg		result[need] = '\0';
52840bd37d32Smrg	    } else {
52850bd37d32Smrg		++need;
52860bd37d32Smrg		result = malloc(need);
52870bd37d32Smrg		if (result == 0) {
52880bd37d32Smrg		    break;
52890bd37d32Smrg		}
52900bd37d32Smrg	    }
52910bd37d32Smrg	}
52920bd37d32Smrg    }
52930bd37d32Smrg    TRACE(("expandFormat(%s) = %s\n", NonNull(format), NonNull(result)));
52940bd37d32Smrg    return result;
52950bd37d32Smrg}
52960bd37d32Smrg
52970bd37d32Smrg/* execute the command after forking.  The main process frees its data */
52980bd37d32Smrgstatic void
52992e4f8982SmrgexecuteCommand(pid_t pid, char **argv)
53000bd37d32Smrg{
53012e4f8982Smrg    (void) pid;
53020bd37d32Smrg    if (argv != 0 && argv[0] != 0) {
53032e4f8982Smrg	char *child_cwd = ProcGetCWD(pid);
53042e4f8982Smrg
53050bd37d32Smrg	if (fork() == 0) {
53062e4f8982Smrg	    if (child_cwd) {
53072e4f8982Smrg		IGNORE_RC(chdir(child_cwd));	/* We don't care if this fails */
53082e4f8982Smrg	    }
53090bd37d32Smrg	    execvp(argv[0], argv);
53100bd37d32Smrg	    exit(EXIT_FAILURE);
53110bd37d32Smrg	}
5312913cc679Smrg	free(child_cwd);
53130bd37d32Smrg    }
53140bd37d32Smrg}
53150bd37d32Smrg
53160bd37d32Smrgstatic void
53170bd37d32SmrgfreeArgv(char *blob, char **argv)
53180bd37d32Smrg{
53190bd37d32Smrg    if (blob) {
53200bd37d32Smrg	free(blob);
53210bd37d32Smrg	if (argv) {
53222e4f8982Smrg	    int n;
53230bd37d32Smrg	    for (n = 0; argv[n]; ++n)
53240bd37d32Smrg		free(argv[n]);
53250bd37d32Smrg	    free(argv);
53260bd37d32Smrg	}
53270bd37d32Smrg    }
53280bd37d32Smrg}
53290bd37d32Smrg
533001037d57Smrgstatic void
533101037d57SmrgreallyExecFormatted(Widget w, char *format, char *data, CELL *start, CELL *finish)
533201037d57Smrg{
533301037d57Smrg    XtermWidget xw;
533401037d57Smrg
533501037d57Smrg    if ((xw = getXtermWidget(w)) != 0) {
533601037d57Smrg	char **argv;
533701037d57Smrg
533801037d57Smrg	if ((argv = tokenizeFormat(format)) != 0) {
53392e4f8982Smrg	    char *blob = argv[0];
53402e4f8982Smrg	    int argc;
53412e4f8982Smrg
534201037d57Smrg	    for (argc = 0; argv[argc] != 0; ++argc) {
534301037d57Smrg		argv[argc] = expandFormat(xw, argv[argc], data, start, finish);
534401037d57Smrg	    }
53452e4f8982Smrg	    executeCommand(TScreenOf(xw)->pid, argv);
534601037d57Smrg	    freeArgv(blob, argv);
534701037d57Smrg	}
534801037d57Smrg    }
534901037d57Smrg}
535001037d57Smrg
53510bd37d32Smrgvoid
53520bd37d32SmrgHandleExecFormatted(Widget w,
535301037d57Smrg		    XEvent *event,
5354e0a2b6dfSmrg		    String *params,	/* selections */
53550bd37d32Smrg		    Cardinal *num_params)
53560bd37d32Smrg{
53570bd37d32Smrg    XtermWidget xw;
53580bd37d32Smrg
535901037d57Smrg    TRACE(("HandleExecFormatted(%d)\n", *num_params));
536001037d57Smrg    if ((xw = getXtermWidget(w)) != 0 &&
536101037d57Smrg	(*num_params > 1)) {
536201037d57Smrg	doSelectionFormat(xw, w, event, params, num_params, reallyExecFormatted);
53630bd37d32Smrg    }
53640bd37d32Smrg}
53650bd37d32Smrg
53660bd37d32Smrgvoid
53670bd37d32SmrgHandleExecSelectable(Widget w,
5368913cc679Smrg		     XEvent *event,
5369e0a2b6dfSmrg		     String *params,	/* selections */
53700bd37d32Smrg		     Cardinal *num_params)
53710bd37d32Smrg{
53720bd37d32Smrg    XtermWidget xw;
53730bd37d32Smrg
53740bd37d32Smrg    if ((xw = getXtermWidget(w)) != 0) {
53750bd37d32Smrg	TRACE(("HandleExecSelectable(%d)\n", *num_params));
53760bd37d32Smrg
53770bd37d32Smrg	if (*num_params == 2) {
53780bd37d32Smrg	    CELL start, finish;
53790bd37d32Smrg	    char *data;
53800bd37d32Smrg	    char **argv;
53810bd37d32Smrg
5382913cc679Smrg	    data = getDataFromScreen(xw, event, params[1], &start, &finish);
53830bd37d32Smrg	    if (data != 0) {
53840bd37d32Smrg		if ((argv = tokenizeFormat(params[0])) != 0) {
53852e4f8982Smrg		    char *blob = argv[0];
53862e4f8982Smrg		    int argc;
53872e4f8982Smrg
53880bd37d32Smrg		    for (argc = 0; argv[argc] != 0; ++argc) {
53890bd37d32Smrg			argv[argc] = expandFormat(xw, argv[argc], data,
53900bd37d32Smrg						  &start, &finish);
53910bd37d32Smrg		    }
53922e4f8982Smrg		    executeCommand(TScreenOf(xw)->pid, argv);
53930bd37d32Smrg		    freeArgv(blob, argv);
53940bd37d32Smrg		}
5395894e0ac8Smrg		free(data);
53960bd37d32Smrg	    }
53970bd37d32Smrg	}
53980bd37d32Smrg    }
53990bd37d32Smrg}
54000bd37d32Smrg
540101037d57Smrgstatic void
540201037d57SmrgreallyInsertFormatted(Widget w, char *format, char *data, CELL *start, CELL *finish)
540301037d57Smrg{
540401037d57Smrg    XtermWidget xw;
540501037d57Smrg
540601037d57Smrg    if ((xw = getXtermWidget(w)) != 0) {
540701037d57Smrg	char *exps;
540801037d57Smrg
540901037d57Smrg	if ((exps = expandFormat(xw, format, data, start, finish)) != 0) {
541001037d57Smrg	    unparseputs(xw, exps);
541101037d57Smrg	    unparse_end(xw);
541201037d57Smrg	    free(exps);
541301037d57Smrg	}
541401037d57Smrg    }
541501037d57Smrg}
541601037d57Smrg
54170bd37d32Smrgvoid
54180bd37d32SmrgHandleInsertFormatted(Widget w,
541901037d57Smrg		      XEvent *event,
5420e0a2b6dfSmrg		      String *params,	/* selections */
54210bd37d32Smrg		      Cardinal *num_params)
54220bd37d32Smrg{
54230bd37d32Smrg    XtermWidget xw;
54240bd37d32Smrg
542501037d57Smrg    TRACE(("HandleInsertFormatted(%d)\n", *num_params));
542601037d57Smrg    if ((xw = getXtermWidget(w)) != 0 &&
542701037d57Smrg	(*num_params > 1)) {
542801037d57Smrg	doSelectionFormat(xw, w, event, params, num_params, reallyInsertFormatted);
54290bd37d32Smrg    }
54300bd37d32Smrg}
54310bd37d32Smrg
54320bd37d32Smrgvoid
54330bd37d32SmrgHandleInsertSelectable(Widget w,
5434913cc679Smrg		       XEvent *event,
5435e0a2b6dfSmrg		       String *params,	/* selections */
54360bd37d32Smrg		       Cardinal *num_params)
54370bd37d32Smrg{
54380bd37d32Smrg    XtermWidget xw;
54390bd37d32Smrg
54400bd37d32Smrg    if ((xw = getXtermWidget(w)) != 0) {
54410bd37d32Smrg	TRACE(("HandleInsertSelectable(%d)\n", *num_params));
54420bd37d32Smrg
54430bd37d32Smrg	if (*num_params == 2) {
54440bd37d32Smrg	    CELL start, finish;
54450bd37d32Smrg	    char *data;
54460bd37d32Smrg	    char *temp = x_strdup(params[0]);
54470bd37d32Smrg
5448913cc679Smrg	    data = getDataFromScreen(xw, event, params[1], &start, &finish);
54490bd37d32Smrg	    if (data != 0) {
54502e4f8982Smrg		char *exps = expandFormat(xw, temp, data, &start, &finish);
54510bd37d32Smrg		if (exps != 0) {
54520bd37d32Smrg		    unparseputs(xw, exps);
545301037d57Smrg		    unparse_end(xw);
54540bd37d32Smrg		    free(exps);
54550bd37d32Smrg		}
54560bd37d32Smrg		free(data);
54570bd37d32Smrg	    }
54580bd37d32Smrg	    free(temp);
54590bd37d32Smrg	}
54600bd37d32Smrg    }
54610bd37d32Smrg}
54620bd37d32Smrg#endif /* OPT_SELECTION_OPS */
5463