"Fossies" - the Fresh Open Source Software Archive

Member "xterm-356/button.c" (2 May 2020, 145059 Bytes) of package /linux/misc/xterm-356.tgz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "button.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 355_vs_356.

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