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