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