home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / vile-src.zip / vile-8.1 / select.c < prev    next >
C/C++ Source or Header  |  1998-10-02  |  34KB  |  1,447 lines

  1. /*
  2.  * select.c        -- selection handling code for vile.
  3.  *
  4.  * Author: Kevin Buettner, Paul Fox
  5.  * Creation: 2/26/94
  6.  *
  7.  * Description:  The following code is an attempt to improve the selection
  8.  * mechanism for vile/xvile.  While my initial goal is to improve selection
  9.  * handling for xvile, there is no reason that this code can not be used on
  10.  * other platforms with some kind of pointing device.  In addition, the
  11.  * mechanism is general enough to be used for other kinds of persistent
  12.  * attributed text.
  13.  *
  14.  * For the purposes of this code, a selection is considered to be a region of
  15.  * text which most applications highlight in some manner.  The user may
  16.  * transfer selected text from one application to another (or even to the
  17.  * same application) in some platform dependent way.  The mechanics of
  18.  * transferring the selection are not dealt with in this file.  Procedures
  19.  * for dealing with the representation are maintained in this file.
  20.  *
  21.  * $Header: /usr/build/vile/vile/RCS/select.c,v 1.80 1998/10/03 02:08:48 tom Exp $
  22.  *
  23.  */
  24.  
  25. #include    "estruct.h"
  26. #include    "edef.h"
  27. #include    "nefunc.h"
  28.  
  29. #define BTN_BEGIN   1
  30. #define BTN_PASTE   2
  31. #define BTN_EXTEND  3
  32.  
  33. #define SEL_BEGIN   10    /* button 1 up */
  34. #define SEL_PASTE   11    /* button 2 up */
  35. #define SEL_EXTEND  12    /* button 3 up */
  36. #define SEL_RELEASE 13    /* click on modeline */
  37. #define SEL_FINISH  14    /* finish a selection */
  38.  
  39. #if OPT_SELECTIONS
  40.  
  41. extern REGION *haveregion;
  42.  
  43. static    void    detach_attrib (BUFFER *bp, AREGION *arp);
  44. static    int    attribute_cntl_a_sequences (void);
  45.  
  46. /*
  47.  * startbufp and startregion are used to represent the start of a selection
  48.  * prior to any highlighted text being displayed.  The start and end of
  49.  * the region will both be where the selection is to start.  Although
  50.  * startregion is attached to the buffer indicated by startbufp, nothing
  51.  * will be displayed since the ar_vattr field is zero.  The reason for
  52.  * attaching it to the buffer is to force the MARKs which represent the
  53.  * start of the region to be updated.
  54.  *
  55.  * selbufp and selregion are used to represent a highlighted selection.
  56.  *
  57.  * When starbufp or selbufp are NULL, the corresponding AREGION (startregion or
  58.  * selregion) is not attached to any buffer and is invalid.
  59.  */
  60.  
  61. static MARK orig_region;
  62. static MARK plus_region;
  63. static BUFFER *    startbufp = NULL;
  64. static AREGION    startregion;
  65. static BUFFER *    selbufp = NULL;
  66. static AREGION    selregion;
  67. #if OPT_HYPERTEXT
  68. static char *    hypercmd;
  69. #endif
  70.  
  71. typedef enum { ORIG_FIXED, END_FIXED, UNFIXED } WHICHEND;
  72.  
  73. static WHICHEND whichend;
  74.  
  75. void
  76. free_attribs(BUFFER *bp)
  77. {
  78.     AREGION *p, *q;
  79.     p = bp->b_attribs;
  80.     while (p != NULL) {
  81.     q = p->ar_next;
  82. #if OPT_HYPERTEXT
  83.     if (p->ar_hypercmd)
  84.         free(p->ar_hypercmd);
  85.         p->ar_hypercmd = 0;
  86. #endif
  87.     if (p == &selregion)
  88.         selbufp = NULL;
  89.     else if (p == &startregion)
  90.         startbufp = NULL;
  91.     else
  92.         free((char *) p);
  93.     p = q;
  94.     }
  95.     bp->b_attribs = NULL;
  96. }
  97.  
  98. void
  99. free_attrib(BUFFER *bp, AREGION *ap)
  100. {
  101.     detach_attrib(bp, ap);
  102. #if OPT_HYPERTEXT
  103.     if (ap->ar_hypercmd) {
  104.     free(ap->ar_hypercmd);
  105.     ap->ar_hypercmd = 0;
  106.     }
  107. #endif
  108.     if (ap == &selregion)
  109.     selbufp = NULL;
  110.     else if (ap == &startregion)
  111.     startbufp = NULL;
  112.     else
  113.     free((char *) ap);
  114. }
  115.  
  116. static void
  117. detach_attrib(BUFFER *bp, AREGION *arp)
  118. {
  119.     if (bp != NULL) {
  120.     WINDOW *wp;
  121.     AREGION **rpp;
  122.     for_each_visible_window(wp) {
  123.         if (wp->w_bufp == bp)
  124.         wp->w_flag |= WFHARD;
  125.     }
  126.     rpp = &bp->b_attribs;
  127.     while (*rpp != NULL) {
  128.         if (*rpp == arp) {
  129.         *rpp = (*rpp)->ar_next;
  130.         arp->ar_region.r_attr_id = 0;
  131.         break;
  132.         }
  133.         else
  134.         rpp = &(*rpp)->ar_next;
  135.     }
  136.     }
  137. }
  138.  
  139. void
  140. find_release_attr(BUFFER *bp, REGION *rp)
  141. {
  142.     if (bp != NULL) {
  143.     AREGION **rpp;
  144.     rpp = &bp->b_attribs;
  145.     while (*rpp != NULL) {
  146.         if ((*rpp)->ar_region.r_attr_id == rp->r_attr_id) {
  147.         free_attrib(bp, *rpp);
  148.         break;
  149.         }
  150.         else
  151.         rpp = &(*rpp)->ar_next;
  152.     }
  153.     }
  154. }
  155.  
  156. int
  157. assign_attr_id(void)
  158. {
  159.     static int attr_id;
  160.     return attr_id++;
  161. }
  162.  
  163. static void
  164. attach_attrib(BUFFER *bp, AREGION *arp)
  165. {
  166.     WINDOW *wp;
  167.     arp->ar_next = bp->b_attribs;
  168.     bp->b_attribs = arp;
  169.     for_each_visible_window(wp)
  170.     if (wp->w_bufp == bp)
  171.         wp->w_flag |= WFHARD;
  172.     arp->ar_region.r_attr_id = (unsigned short) assign_attr_id();
  173. }
  174.  
  175. /*
  176.  * Adjusts dot to last char of last line if dot is past end of buffer.  This
  177.  * can happen when selecting with the mouse.
  178.  */
  179.  
  180. static void
  181. fix_dot(void)
  182. {
  183.     if (is_header_line(DOT, curwp->w_bufp)) {
  184.     DOT.l = lback(DOT.l);
  185.     DOT.o = llength(DOT.l);
  186.     }
  187. }
  188.  
  189. /*
  190.  * Output positional information regarding the selection to the message line.
  191.  */
  192.  
  193. static void
  194. show_selection_position(int yanked)
  195. {
  196. #ifdef WMDTERSELECT
  197.     if (!w_val(curwp, WMDTERSELECT)) {
  198.     mlwrite("(%d,%d) thru (%d,%d) %s",
  199.         line_no(selbufp, selregion.ar_region.r_orig.l),
  200.         getcol(selregion.ar_region.r_orig, FALSE) + 1,
  201.         line_no(selbufp, selregion.ar_region.r_end.l),
  202.         getcol(selregion.ar_region.r_end, FALSE),
  203.         yanked ? "yanked" : "selected");
  204.  
  205.     }
  206. #endif /* WMDTERSELECT */
  207. }
  208.  
  209. /* Start a selection at dot */
  210. int
  211. sel_begin(void)
  212. {
  213.     fix_dot();
  214.     detach_attrib(startbufp, &startregion);
  215.     plus_region =
  216.     orig_region =
  217.     startregion.ar_region.r_orig =
  218.     startregion.ar_region.r_end  = DOT;
  219.     plus_region.o += 1;
  220.     startregion.ar_vattr = 0;
  221.     startregion.ar_shape = EXACT;
  222. #if OPT_HYPERTEXT
  223.     startregion.ar_hypercmd = 0;
  224. #endif
  225.     startbufp = curwp->w_bufp;
  226.     attach_attrib(startbufp, &startregion);
  227.     whichend = UNFIXED;
  228.     return TRUE;
  229. }
  230.  
  231. static int
  232. dot_vs_mark(void)
  233. {
  234.     int    cmp = line_no(curbp, DOT.l) - line_no(curbp, MK.l);
  235.     if (cmp == 0)
  236.         cmp = DOT.o - MK.o;
  237.     return cmp;
  238. }
  239.  
  240. /* Extend the current selection to dot */
  241. int
  242. sel_extend(int wiping, int include_dot)
  243. {
  244.     REGIONSHAPE save_shape = regionshape;
  245.     REGION a,b;
  246.     WINDOW *wp;
  247.     MARK saved_dot;
  248.     MARK working_dot;
  249.  
  250.     saved_dot = DOT;
  251.     if (startbufp != NULL) {
  252.         detach_attrib(selbufp, &selregion);
  253.         selbufp = startbufp;
  254.         selregion = startregion;
  255.         attach_attrib(selbufp, &selregion);
  256.         detach_attrib(startbufp, &startregion);
  257.         startbufp = NULL;
  258.     }
  259.  
  260.     if (curwp->w_bufp != selbufp)
  261.         return FALSE;        /* handles NULL case also */
  262.  
  263.     fix_dot();
  264.     regionshape = selregion.ar_shape;
  265.  
  266.     if (wiping && whichend == END_FIXED)
  267.         MK = selregion.ar_region.r_end;
  268.     else
  269.         MK = selregion.ar_region.r_orig;
  270.  
  271.     /* FIXME: Make sure DOT and MK are in the same buffer */
  272.  
  273.     /*
  274.      * If we're extending in the positive direction, we want to include DOT
  275.      * in the selection.  To include DOT, we must advance it one char since
  276.      * a region runs from r_orig up to but not including r_end.
  277.      */
  278.     working_dot = DOT;
  279.     if (include_dot
  280.      && (selregion.ar_shape == EXACT)
  281.      && dot_vs_mark() >= 0) {
  282.         if (samepoint(MK, orig_region)) {
  283.             DOT.o += 1;
  284.         } else if (samepoint(MK, plus_region)) {
  285.             DOT.o += 1;
  286.             MK = orig_region;
  287.         }
  288.     }
  289.     if (getregion(&a) == FALSE) {
  290.         return FALSE;
  291.     }
  292.     DOT = working_dot;
  293.  
  294.     /*
  295.      * Build a second region in the "opposite" direction.
  296.      */
  297.     if (wiping && whichend == ORIG_FIXED)
  298.         MK = selregion.ar_region.r_orig;
  299.     else
  300.         MK = selregion.ar_region.r_end;
  301.  
  302.     if (include_dot) {
  303.         if (selregion.ar_shape == EXACT) {
  304.             if (dot_vs_mark() <= 0) {
  305.                 if (samepoint(MK, orig_region))
  306.                     MK.o += 1;
  307.             }
  308.         } else if (selregion.ar_shape == RECTANGLE) {
  309.             if (samepoint(MK, DOT)) { /* avoid making empty-region */
  310.                 MK =  orig_region;
  311.                 DOT = plus_region;
  312.             }
  313.         }
  314.     }
  315.     if (getregion(&b) == FALSE) {
  316.         return FALSE;
  317.     }
  318.  
  319.     /*
  320.      * The two regions, 'a' and 'b' are _usually_ identical, except for the
  321.      * special case where we've extended one to the right to include the
  322.      * right endpoint of the region.
  323.      *
  324.      * For EXACT selections, setting 'whichend' to ORIG_FIXED means that
  325.      * we're selecting from the anchor point right/down.  Conversely,
  326.      * setting it to END_FIXED means that we selecting left/up.
  327.      *
  328.      * Rectangles are specified by making MK the opposite corner from DOT.
  329.      * If DOT is below MK, we'll say that the selection region is
  330.      * ORIG_FIXED so that the next call on this function will build the
  331.      * regions a/b consistently.
  332.      *
  333.      * If the regions a/b are empty, we've made a mistake; this will cause
  334.      * the selection to be dropped in xvile.
  335.      */
  336.  
  337.     if (a.r_size > b.r_size) {
  338.         whichend = ORIG_FIXED;
  339.         selregion.ar_region = a;
  340.     }
  341.     else {
  342.         if (selregion.ar_shape == RECTANGLE) {
  343.             if (dot_vs_mark() < 0)
  344.                 whichend = END_FIXED;
  345.             else
  346.                 whichend = ORIG_FIXED;
  347.         } else {    /* exact or full-line */
  348.             whichend = END_FIXED;
  349.         }
  350.         selregion.ar_region = b;
  351.     }
  352.  
  353.     selregion.ar_vattr = VASEL | VOWN_SELECT;
  354.     for_each_visible_window(wp) {
  355.         if (wp->w_bufp == selbufp)
  356.             wp->w_flag |= WFHARD;
  357.     }
  358.  
  359.     show_selection_position(FALSE);
  360.  
  361.     regionshape = save_shape;
  362.     DOT = saved_dot;
  363.     OWN_SELECTION();
  364.     return TRUE;
  365. }
  366.  
  367. /*
  368.  * Detach current selection (if attached) and null the associated buffer
  369.  * pointer.
  370.  */
  371. void
  372. sel_release(void)
  373. {
  374.     detach_attrib(selbufp, &selregion);
  375.     selbufp = NULL;
  376. }
  377.  
  378. /*
  379.  * Assert/reassert ownership of selection if appropriate.  This is necessary
  380.  * in order to paste a selection after it's already been pasted once and then
  381.  * modified.
  382.  */
  383.  
  384. void
  385. sel_reassert_ownership(BUFFER *bp)
  386. {
  387.     if (selbufp == bp) {
  388.     OWN_SELECTION();
  389.     }
  390. }
  391.  
  392. #if OPT_SEL_YANK
  393. /*
  394.  * Yank the selection.  Return TRUE if selection could be yanked, FALSE
  395.  * otherwise.  Note that this code will work even if the buffer being
  396.  * yanked from is not attached to any window since it creates its own
  397.  * fake window in order to perform the yanking.
  398.  */
  399.  
  400. int
  401. sel_yank(int reg)
  402. {
  403.     REGIONSHAPE save_shape;
  404.     WINDOW *save_wp;
  405.  
  406.     if (selbufp == NULL)
  407.     return FALSE;            /* No selection to yank */
  408.  
  409.     if ((save_wp = push_fake_win(selbufp)) == NULL)
  410.     return FALSE;
  411.  
  412.     save_shape = regionshape;
  413.     ukb = reg;
  414.     kregflag = 0;
  415.     haveregion = &selregion.ar_region;
  416.     regionshape = selregion.ar_shape;
  417.     yankregion();
  418.     haveregion = NULL;
  419.     regionshape = save_shape;
  420.     pop_fake_win(save_wp);
  421.     show_selection_position(TRUE);
  422.  
  423.     /* put cursor back on screen...is there a cheaper way to do this?  */
  424.     (void)update(FALSE);
  425.     return TRUE;
  426. }
  427.  
  428. #if NEEDED
  429. int
  430. sel_attached(void)
  431. {
  432.     return startbufp == NULL;
  433. }
  434. #endif  /* NEEDED */
  435.  
  436. BUFFER *
  437. sel_buffer(void)
  438. {
  439.     return (startbufp != NULL) ? startbufp : selbufp;
  440. }
  441. #endif  /* OPT_SEL_YANK */
  442.  
  443. int
  444. sel_setshape(REGIONSHAPE shape)
  445. {
  446.     if (startbufp != NULL) {
  447.     startregion.ar_shape = shape;
  448.     return TRUE;
  449.     }
  450.     else if (selbufp != NULL) {
  451.     selregion.ar_shape = shape;
  452.     return TRUE;
  453.     }
  454.     else {
  455.     return FALSE;
  456.     }
  457. }
  458.  
  459. /* return a region which goes from DOT to the far end of the current
  460.     selection region.  shape is maintained.  returns pointer to static
  461.     region struct.
  462. */
  463. static REGION *
  464. extended_region(void)
  465. {
  466.     REGION *rp = NULL;
  467.     static REGION a, b;
  468.     MARK savemark;
  469.  
  470.     savemark = MK;
  471.     regionshape = selregion.ar_shape;
  472.     MK = selregion.ar_region.r_orig;
  473.     DOT.o += 1;
  474.     if (getregion(&a) == TRUE) {
  475.     DOT.o -= 1;
  476.     MK = selregion.ar_region.r_end;
  477.     if (regionshape == FULLLINE)
  478.         MK.l = lback(MK.l);
  479.     /* region b is to the end of the selection */
  480.     if (getregion(&b) == TRUE) {
  481.         /* if a is bigger, it's the one we want */
  482.         if (a.r_size > b.r_size)
  483.         rp = &a;
  484.         else
  485.         rp = &b;
  486.     }
  487.     } else {
  488.     DOT.o -= 1;
  489.     }
  490.     MK = savemark;
  491.     return rp;
  492. }
  493.  
  494. static    int    doingopselect;
  495.  
  496. /* ARGSUSED */
  497. int
  498. sel_motion(int f GCC_UNUSED, int n GCC_UNUSED)
  499. {
  500.     if (selbufp == NULL) {
  501.     mlwrite("[No selection exists.]");
  502.     return FALSE;
  503.     }
  504.  
  505.     if (selbufp != curbp) {
  506.     /* FIXME -- sure would be nice if we could do non-destructive
  507.         things to other buffers, mainly yank. */
  508.     mlwrite("[Selection not in current buffer.]");
  509.     return FALSE;
  510.     }
  511.  
  512.     curwp->w_flag |= WFMOVE;
  513.  
  514.     /* if this is happening on behalf of an operator, we're pretending
  515.      * that the motion took us from one end of the selection to the
  516.      * other, unless we're trying to select to the selection, in which
  517.      * case that would be self-defeating
  518.      */
  519.     if (doingopcmd && !doingopselect) {
  520.     pre_op_dot = selregion.ar_region.r_orig;  /* move us there */
  521.     haveregion = &selregion.ar_region;
  522.     regionshape = selregion.ar_shape;
  523.     return TRUE;
  524.     }
  525.  
  526.     if (!doingopcmd) { /* it's a simple motion -- go to the top of selection */
  527.     /* remember -- this can never be used with an operator, as in
  528.      * "delete to the selection", since that case is taken care
  529.      * of above, and is really the whole reason for this
  530.      * "motion" in the first place.  */
  531.     DOT = selregion.ar_region.r_orig;  /* move us there */
  532.     return TRUE;
  533.     }
  534.  
  535.     /* we must be doing an extension */
  536.     haveregion = extended_region();
  537.     return haveregion ? TRUE:FALSE;
  538.  
  539. }
  540.  
  541. static int
  542. selectregion(void)
  543. {
  544.     register int    status;
  545.     REGION        region;
  546.     MARK        savedot;
  547.     MARK        savemark;
  548.     int        hadregion = FALSE;
  549.  
  550.     savedot = DOT;
  551.     savemark = MK;
  552.     if (haveregion) {    /* getregion() will clear this, so
  553.                     we need to save it */
  554.         region = *haveregion;
  555.         hadregion = TRUE;
  556.     }
  557.     status = yankregion();
  558.     DOT = savedot;
  559.     MK = savemark;
  560.     if (status != TRUE)
  561.         return status;
  562.     if (hadregion || ((status = getregion(®ion)) == TRUE)) {
  563.         detach_attrib(startbufp, &startregion);
  564.         detach_attrib(selbufp, &selregion);
  565.         selbufp = curbp;
  566.         selregion.ar_region = region;
  567.         selregion.ar_vattr = VASEL | VOWN_SELECT;
  568.         selregion.ar_shape = regionshape;
  569. #if OPT_HYPERTEXT
  570.         selregion.ar_hypercmd = 0;
  571. #endif
  572.         attach_attrib(selbufp, &selregion);
  573.         OWN_SELECTION();
  574.     }
  575.     return status;
  576. }
  577.  
  578. static void
  579. sweepmsg(const char *msg)
  580. {
  581.     char    temp[NLINE];
  582.     (void)kcod2pstr(fnc2kcod(&f_multimotion), temp);
  583.     mlforce("[%s (end with %*S)]", msg, *temp,temp+1);
  584. }
  585.  
  586. static int
  587. release_selection(int status)
  588. {
  589.     TRACE(("MOUSE release selection\n"))
  590.     if (doingsweep) {
  591.         doingsweep = FALSE;
  592.         if (status != TRUE)
  593.             mlforce("[Sweeping: Aborted]");
  594.         else
  595.             mlerase();
  596.     }
  597.     sel_release();
  598.     return status;
  599. }
  600.  
  601. #if OPT_MOUSE
  602. static int
  603. paste_selection(void)
  604. {
  605.     if (!doingsweep) {
  606.         TRACE(("MOUSE paste selection\n"))
  607.         mayneedundo();
  608.         return putafter(FALSE, 1);
  609.     }
  610.     return SEL_PASTE;
  611. }
  612.  
  613. /*
  614.  * On button press, we get an explicit number (1,2,3), and on release we don't
  615.  * really know which button, but assume it is the last-pressed button.
  616.  */
  617. int
  618. on_mouse_click(int button, int y, int x)
  619. {
  620.     static int first_x, first_y, pending;
  621.     WINDOW *this_wp, *that_wp;
  622.     int status;
  623.  
  624.     if (button > 0) {
  625.         if ((this_wp = row2window(y)) != 0
  626.          && (y != mode_row(this_wp))) {
  627.             /*
  628.              * If we get a click on the "<" marking the left side
  629.              * of a shifted window, force the screen right. This
  630.              * makes it more consistent if there's a tab.
  631.              */
  632.             if (w_val(this_wp, WVAL_SIDEWAYS)
  633.              && x == 0) {
  634.                 mvleftwind(FALSE, 1);
  635.             }
  636.             if (!doingsweep) {
  637.                 if (button == BTN_EXTEND) {
  638.                     first_x = offs2col(this_wp, this_wp->w_dot.l, this_wp->w_dot.o);
  639.                     first_y = line_no(this_wp->w_bufp, this_wp->w_dot.l)
  640.                         - line_no(this_wp->w_bufp, this_wp->w_line.l);
  641.                 } else {
  642.                     first_x = x;
  643.                     first_y = y;
  644.                 }
  645.             }
  646.             status = setcursor(y, x);
  647.             /*
  648.              * Check for button1-down while we're in multimotion
  649.              * sweep, so we can suppress highlighting extension.
  650.              */
  651.             if (button != BTN_EXTEND
  652.              && status == TRUE
  653.              && doingsweep) {
  654.                 status = SORTOFTRUE;
  655.                 if (button == BTN_BEGIN) {
  656.                     first_x = x;
  657.                     first_y = y;
  658.                 }
  659.             }
  660.         } else { /* pressed button on modeline */
  661.             status = SORTOFTRUE;
  662.             first_x = x;
  663.             first_y = y;
  664.         }
  665.         pending = button;
  666.     } else if (pending) {
  667.         button  = pending;
  668.         pending = FALSE;
  669.         this_wp = row2window(y);
  670.         that_wp = row2window(first_y);
  671.         if (this_wp == 0
  672.          || that_wp == 0
  673.          || reading_msg_line) {
  674.             TRACE(("MOUSE cannot move msg-line\n"))
  675.             status = FALSE;
  676.         } else if (insertmode
  677.          && (this_wp != curwp || that_wp != curwp)) {
  678.             TRACE(("MOUSE cannot move from window while inserting\n"))
  679.             kbd_alarm();
  680.             status = ABORT;
  681.         } else if (first_y == mode_row(that_wp)) { /* drag modeline? */
  682.             if (first_y == y) {
  683.                 sel_release();
  684.                 status = SEL_RELEASE;
  685.             } else {
  686.                 WINDOW *save_wp = curwp;
  687.                 TRACE(("MOUSE dragging modeline\n"))
  688.                 set_curwp(that_wp);
  689.                 status = shrinkwind(FALSE, first_y - y);
  690.                 set_curwp(save_wp);
  691.             }
  692.         } else if (y != first_y || x != first_x) { /* drag selection */
  693.             if (button == BTN_PASTE) {
  694.                 (void) setcursor(y, x);
  695.                 status = paste_selection();
  696.             } else if (doingsweep) {
  697.                 switch (button) {
  698.                 case BTN_BEGIN:
  699.                     (void) release_selection(TRUE);
  700.                     status = setcursor(first_y, first_x);
  701.                     if (status == TRUE) {
  702.                         MK = DOT;
  703.                         status = SEL_BEGIN;
  704.                         TRACE(("MOUSE setting SEL_BEGIN MK %d.%d\n",
  705.                             line_no(curbp, MK.l), MK.o))
  706.                     }
  707.                     break;
  708.                 case BTN_PASTE:
  709.                     (void) setcursor(y, x);
  710.                     status = paste_selection();
  711.                     break;
  712.                 default:
  713.                     (void) setcursor(y, x);
  714.                     status = SEL_EXTEND;
  715.                     TRACE(("MOUSE setting SEL_EXTEND DOT %d.%d MK %d.%d\n",
  716.                         line_no(curbp, MK.l), MK.o,
  717.                         line_no(curbp, DOT.l), DOT.o))
  718.                     break;
  719.                 }
  720.             } else {
  721.                 TRACE(("MOUSE begin multimotion on button%d-up\n", button))
  722.                 if (button == BTN_EXTEND) {
  723.                     (void) setcursor(y, x);
  724.                     y = first_y;
  725.                     x = first_x;
  726.                 }
  727.                 doingsweep = SORTOFTRUE;
  728.                 (void)sel_begin();
  729.                 (void)sel_setshape(EXACT);
  730.                 status = setcursor(y, x);
  731.                 status = multimotion(TRUE,1);
  732.                 TRACE(("MOUSE end multimotion after button%d-up\n", button))
  733.                 if (status == SEL_PASTE)
  734.                     status = paste_selection();
  735.             }
  736.         } else { /* position the cursor */
  737.             TRACE(("MOUSE button %d position cursor\n", button))
  738.             (void) setcursor(y, x);
  739.             switch (button) {
  740.             case BTN_BEGIN:
  741.                 status = SEL_FINISH;
  742.                 break;
  743.             case BTN_PASTE:
  744.                 status = paste_selection();
  745.                 break;
  746.             default:
  747.                 status = release_selection(TRUE);
  748.                 break;
  749.             }
  750.         }
  751.     } else {
  752.         TRACE(("MOUSE ignored (illegal state)\n"))
  753.         status = FALSE;
  754.     }
  755.  
  756.     if (status == TRUE || status >= SORTOFTRUE)
  757.         (void)update(TRUE);
  758.  
  759.     TRACE(("MOUSE status:%d\n", status))
  760.     return status;
  761. }
  762. #endif
  763.  
  764. int
  765. multimotion(int f, int n)
  766. {
  767.     const CMDFUNC    *cfp;
  768.     int s, c, waserr;
  769.     int pasting;
  770.     REGIONSHAPE shape;
  771.     MARK savedot;
  772.     MARK savemark;
  773.     MARK realdot;
  774.     BUFFER *origbp = curbp;
  775.     static int wassweephack = FALSE;
  776.  
  777.     /* Use the repeat-count as a shortcut to specify the type of selection.
  778.      * I'd use int-casts of the enum value, but declaring enums with
  779.      * specific values isn't 100% portable.
  780.      */
  781.     if (!f || n <= 0)
  782.         n = 1;
  783.     if (n == 3)
  784.         regionshape = RECTANGLE;
  785.     else if (n == 2)
  786.         regionshape = FULLLINE;
  787.     else
  788.         regionshape = EXACT;
  789.     shape = regionshape;
  790.  
  791.     sweephack = FALSE;
  792.     savedot = DOT;
  793.     switch (doingsweep) {
  794.     case TRUE:    /* the same command terminates as starts the sweep */
  795.         doingsweep = FALSE;
  796.         mlforce("[Sweeping: Completed]");
  797.         regionshape = shape;
  798.         /* since the terminating 'q' is executed as a motion, we have
  799.            now lost the value of sweephack we were interested in, the
  800.            one that tells us to include DOT.o in the selection.
  801.            so we preserved it in wassweephack, and restore it here.
  802.          */
  803.         if (wassweephack)
  804.             sweephack = wassweephack;
  805.         return TRUE;
  806.     case SORTOFTRUE:
  807.         doingsweep = TRUE;
  808.         sweepmsg("Begin cursor sweep...");
  809.         sel_extend(TRUE,(regionshape != RECTANGLE && sweephack));
  810.         savedot = MK;
  811.         TRACE(("MOUSE BEGIN DOT: %d.%d MK %d.%d\n",
  812.             line_no(curbp, DOT.l), DOT.o,
  813.             line_no(curbp, MK.l), MK.o))
  814.         break;
  815.     case FALSE:
  816.         doingsweep = TRUE;
  817.         sweepmsg("Begin cursor sweep...");
  818.         (void)sel_begin();
  819.         (void)sel_setshape(shape);
  820.         break;
  821.     }
  822.  
  823.     waserr = TRUE; /* to force message "state-machine" */
  824.     realdot = DOT;
  825.     pasting = FALSE;
  826.  
  827.     while (doingsweep) {
  828.  
  829.         /* Fix up the screen    */
  830.         s = update(FALSE);
  831.  
  832.         /* get the next command from the keyboard */
  833.         c = kbd_seq();
  834.  
  835.         if (ABORTED(c)
  836.          || curbp != origbp) {
  837.             return release_selection(FALSE);
  838.         }
  839.  
  840.         f = FALSE;
  841.         n = 1;
  842.  
  843.         do_repeats(&c,&f,&n);
  844.  
  845.         /* and execute the command */
  846.         cfp = kcod2fnc(c);
  847.         if ( (cfp != NULL)
  848.          && ((cfp->c_flags & MOTION) != 0)) {
  849.             MARK testdot;
  850.  
  851.             wassweephack = sweephack;
  852.             sweephack = FALSE;
  853.             TRACE(("MOUSE TEST DOT: %d.%d MK %d.%d\n",
  854.                 line_no(curbp, DOT.l), DOT.o,
  855.                 line_no(curbp, MK.l), MK.o))
  856.             testdot = DOT;
  857.  
  858.             s = execute(cfp, f, n);
  859.             switch (s) {
  860.             case SEL_RELEASE:
  861.                 TRACE(("MOUSE SEL_RELEASE %d.%d\n",
  862.                     line_no(curbp, DOT.l), DOT.o))
  863.                 return release_selection(TRUE);
  864.  
  865.             case SEL_PASTE:
  866.                 pasting = TRUE;
  867.                 /* FALLTHRU */
  868.  
  869.             case SEL_FINISH:
  870.                 doingsweep = FALSE;
  871.                 break;
  872.  
  873.             case SORTOFTRUE:
  874.                 TRACE(("MOUSE selection pending %d.%d -> %d.%d\n",
  875.                     line_no(curbp, realdot.l), realdot.o,
  876.                     line_no(curbp, testdot.l), testdot.o))
  877.                 realdot = testdot;
  878.                 break;
  879.  
  880.             case SEL_BEGIN:
  881.                 savedot = MK;
  882.                 TRACE(("MOUSE SEL_BEGIN...\n"))
  883.                 /*FALLTHRU*/
  884.  
  885.             case SEL_EXTEND:
  886.                 TRACE(("MOUSE SEL_EXTEND from %d.%d to %d.%d\n",
  887.                     line_no(curbp, savedot.l), savedot.o,
  888.                     line_no(curbp, DOT.l), DOT.o))
  889.                 /*FALLTHRU*/
  890.  
  891.             case TRUE:
  892.                 if (waserr && doingsweep) {
  893.                     sweepmsg("Sweeping...");
  894.                     waserr = FALSE;
  895.                 }
  896.                 realdot = DOT;
  897.                 DOT = savedot;
  898.                 (void)sel_begin();
  899.                 DOT = realdot;
  900.                 TRACE(("MOUSE LOOP save: %d.%d real %d.%d, mark %d.%d\n",
  901.                     line_no(curbp, savedot.l), savedot.o,
  902.                     line_no(curbp, realdot.l), realdot.o,
  903.                     line_no(curbp, MK.l), MK.o))
  904.                 (void)sel_setshape(shape);
  905.                 /* we sometimes want to include DOT.o in the
  906.                    selection (unless it's a rectangle, in
  907.                    which case it's taken care of elsewhere)
  908.                  */
  909.                 sel_extend(TRUE,(regionshape != RECTANGLE &&
  910.                     sweephack));
  911.                 break;
  912.  
  913.             default:
  914.                 sweepmsg("Sweeping: Motion failed.");
  915.                 waserr = TRUE;
  916.                 break;
  917.             }
  918.          } else {
  919.             sweepmsg("Sweeping: Only motions permitted");
  920.             waserr = TRUE;
  921.          }
  922.  
  923.     }
  924.     regionshape = shape;
  925.     /* if sweephack is set here, it's because the last motion had
  926.         it set */
  927.     if (doingopcmd)
  928.         pre_op_dot = savedot;
  929.  
  930.     savedot = DOT;
  931.     savemark = MK;
  932.     DOT = realdot;
  933.     TRACE(("MOUSE SAVE DOT: %d.%d MK %d.%d\n",
  934.         line_no(curbp, DOT.l), DOT.o,
  935.         line_no(curbp, MK.l), MK.o))
  936.     if ((regionshape != RECTANGLE) && sweephack) {
  937.         if (dot_vs_mark() < 0)
  938.             MK.o += 1;
  939.         else
  940.             DOT.o += 1;
  941.     }
  942.     s = yankregion();
  943.     DOT = savedot;
  944.     MK = savemark;
  945.  
  946.     sweephack = wassweephack = FALSE;
  947.  
  948.     if (s == TRUE && pasting)
  949.         s = SEL_PASTE;
  950.         
  951.     return s;
  952. }
  953.  
  954. /*ARGSUSED*/
  955. int
  956. multimotionfullline(int f GCC_UNUSED, int n GCC_UNUSED)
  957. {
  958.     return multimotion(TRUE,2);
  959. }
  960.  
  961. /*ARGSUSED*/
  962. int
  963. multimotionrectangle(int f GCC_UNUSED, int n GCC_UNUSED)
  964. {
  965.     return multimotion(TRUE,3);
  966. }
  967.  
  968. #if OPT_PERL || OPT_TCL
  969. BUFFER *
  970. get_selection_buffer_and_region(AREGION *arp)
  971. {
  972.     if (selbufp) {
  973.     *arp = selregion;
  974.     }
  975.     return selbufp;
  976. }
  977. #endif
  978.  
  979. int
  980. attributeregion(void)
  981. {
  982.     register int    status;
  983.     REGION        region;
  984.     AREGION *    arp;
  985.  
  986.     if ((status = getregion(®ion)) == TRUE) {
  987.         if (VATTRIB(videoattribute) != 0
  988. #if OPT_HYPERTEXT
  989.         || hypercmd != 0)
  990. #endif
  991.         {
  992.         /* add new attribute-region */
  993.         if ((arp = typealloc(AREGION)) == NULL) {
  994.             (void)no_memory("AREGION");
  995.             return FALSE;
  996.         }
  997.         arp->ar_region = region;
  998.         arp->ar_vattr = videoattribute; /* include ownership */
  999.         arp->ar_shape = regionshape;
  1000. #if OPT_HYPERTEXT
  1001.         arp->ar_hypercmd = hypercmd;    /* already malloc'd for us */
  1002.         hypercmd = 0;            /* reset it for future calls */
  1003. #endif
  1004.         attach_attrib(curbp, arp);
  1005.         } else { /* purge attributes in this region */
  1006.         L_NUM rls = line_no(curbp, region.r_orig.l);
  1007.         L_NUM rle = line_no(curbp, region.r_end.l);
  1008.         C_NUM ros = region.r_orig.o;
  1009.         C_NUM roe = region.r_end.o;
  1010.         AREGION *p, *q, *n;
  1011.         int owner;
  1012.  
  1013.         owner = VOWNER(videoattribute);
  1014.  
  1015.         for (p = curbp->b_attribs, q = 0; p != 0; p = q) {
  1016.             L_NUM pls, ple;
  1017.             C_NUM pos, poe;
  1018.  
  1019.             q = p->ar_next;
  1020.  
  1021.             if (owner != 0 && owner != VOWNER(p->ar_vattr))
  1022.             continue;
  1023.  
  1024.             pls = line_no(curbp, p->ar_region.r_orig.l);
  1025.             ple = line_no(curbp, p->ar_region.r_end.l);
  1026.             pos = p->ar_region.r_orig.o;
  1027.             poe = p->ar_region.r_end.o;
  1028.  
  1029.             /* Earlier the overlapping region check was made based only
  1030.              * on line numbers and so was right only for FULLINES shape
  1031.              * changed it to be correct for EXACT and RECTANGLE also
  1032.              * -kuntal 9/13/98
  1033.              */
  1034.             /*
  1035.              * check for overlap:
  1036.              * for any shape of region 'p' things are fine as long as
  1037.              * 'region' is above or below it
  1038.              */
  1039.             if (ple < rls || pls > rle)
  1040.             continue;
  1041.             /*
  1042.              * for EXACT 'p' region
  1043.              */
  1044.             if ( p->ar_shape == EXACT ) {
  1045.             if ( ple == rls && poe-1 < ros )
  1046.                 continue;
  1047.             if ( pls == rle && pos > roe )
  1048.                 continue;
  1049.             }
  1050.             /*
  1051.              * for RECTANGLE 'p' region
  1052.              */
  1053.             if ( p->ar_shape == RECTANGLE )
  1054.             if (poe < ros || pos > roe)
  1055.                 continue;
  1056.  
  1057.             /*
  1058.              * FIXME: this removes the whole of an overlapping region;
  1059.              * we really only want to remove the overlapping portion...
  1060.              */
  1061.  
  1062.             /*
  1063.              * we take care of this fix easily as long as neither of
  1064.              * 'p' or 'region' are RECTANGLE. we will need to create
  1065.              * at the most one new region in case 'region' is
  1066.              * completely contained within 'p'
  1067.              */
  1068.             if (p->ar_shape != RECTANGLE && regionshape != RECTANGLE) {
  1069.             if ((rls > pls) || (rls == pls && ros > pos)) {
  1070.                 p->ar_shape = EXACT;
  1071.                 if ((rle < ple) || (rle == ple && roe < poe)) {
  1072.                 /* open a new region */
  1073.                 if ((n = typealloc(AREGION)) == NULL) {
  1074.                     (void)no_memory("AREGION");
  1075.                     return FALSE;
  1076.                 }
  1077.                 n->ar_region = p->ar_region;
  1078.                 n->ar_vattr  = p->ar_vattr;
  1079.                 n->ar_shape  = p->ar_shape;
  1080. #if OPT_HYPERTEXT
  1081.                 n->ar_hypercmd = p->ar_hypercmd;
  1082. #endif
  1083.                 n->ar_region.r_orig.l=(region.r_end.l);
  1084.                 n->ar_region.r_orig.o=(region.r_end.o);
  1085.                 attach_attrib(curbp, n);
  1086.                 }
  1087.                 p->ar_region.r_end.l = (region.r_orig.l);
  1088.                 p->ar_region.r_end.o = (region.r_orig.o);
  1089.                 /*
  1090.                 if (p->ar_region.r_orig.o == 0 &&
  1091.                 p->ar_region.r_end.o
  1092.                   == w_left_margin(curwp) ) {
  1093.                 p->ar_shape == FULLLINE;
  1094.                 }
  1095.                 if (n->ar_region.r_orig.o == 0 &&
  1096.                 n->ar_region.r_end.o
  1097.                   == w_left_margin(curwp) ) {
  1098.                 n->ar_shape == FULLLINE;
  1099.                 }
  1100.                 */
  1101.                 curwp->w_flag |= WFHARD;
  1102.                 continue;
  1103.             } else if ((rle < ple) || (rle == ple && roe < poe)) {
  1104.                 p->ar_region.r_orig.l = (region.r_end.l);
  1105.                 p->ar_region.r_orig.o = (region.r_end.o);
  1106.                 curwp->w_flag |= WFHARD;
  1107.                 continue;
  1108.             }
  1109.             }
  1110.  
  1111.             detach_attrib(curbp, p);
  1112.         }
  1113.         }
  1114.     }
  1115.     return status;
  1116. }
  1117.  
  1118. int
  1119. attributeregion_in_region(REGION *rp,
  1120.               REGIONSHAPE shape,
  1121.               VIDEO_ATTR vattr,
  1122.               char *hc)
  1123. {
  1124.     haveregion = rp;
  1125.     DOT =  rp->r_orig;
  1126.     MK = rp->r_end;
  1127.     if (shape == FULLLINE)
  1128.     MK.l = lback(MK.l);
  1129.     regionshape = shape;    /* Not that the following actually cares */
  1130.     videoattribute = vattr;
  1131. #if OPT_HYPERTEXT
  1132.     hypercmd = hc;
  1133. #endif /* OPT_HYPERTEXT */
  1134.     return attributeregion();
  1135. }
  1136.  
  1137. int
  1138. operselect(int f, int n)
  1139. {
  1140.     int s;
  1141.     opcmd = OPOTHER;
  1142.     doingopselect = TRUE;
  1143.     s = vile_op(f,n,selectregion,"Select");
  1144.     doingopselect = FALSE;
  1145.     return s;
  1146. }
  1147.  
  1148. int
  1149. operattrbold(int f, int n)
  1150. {
  1151.       opcmd = OPOTHER;
  1152.       videoattribute = VABOLD | VOWN_OPERS;
  1153.       return vile_op(f,n,attributeregion,"Set bold attribute");
  1154. }
  1155.  
  1156. int
  1157. operattrital(int f, int n)
  1158. {
  1159.       opcmd = OPOTHER;
  1160.       videoattribute = VAITAL | VOWN_OPERS;
  1161.       return vile_op(f,n,attributeregion,"Set italic attribute");
  1162. }
  1163.  
  1164. int
  1165. operattrno(int f, int n)
  1166. {
  1167.       opcmd = OPOTHER;
  1168.       videoattribute = 0;    /* clears no matter who "owns" */
  1169.       return vile_op(f,n,attributeregion,"Set normal attribute");
  1170. }
  1171.  
  1172. int
  1173. operattrrev(int f, int n)
  1174. {
  1175.       opcmd = OPOTHER;
  1176.       videoattribute = VAREV | VOWN_OPERS;
  1177.       return vile_op(f,n,attributeregion,"Set reverse attribute");
  1178. }
  1179.  
  1180. int
  1181. operattrul(int f, int n)
  1182. {
  1183.       opcmd = OPOTHER;
  1184.       videoattribute = VAUL | VOWN_OPERS;
  1185.       return vile_op(f,n,attributeregion,"Set underline attribute");
  1186. }
  1187.  
  1188. #if OPT_HYPERTEXT
  1189. static int
  1190. attributehyperregion(void)
  1191. {
  1192.     char line[NLINE];
  1193.     int  status;
  1194.  
  1195.     line[0] = 0;
  1196.     status = mlreply_no_opts("Hypertext Command: ", line, NLINE);
  1197.  
  1198.     if (status != TRUE)
  1199.     return status;
  1200.  
  1201.     hypercmd = strmalloc(line);
  1202.     return attributeregion();
  1203. }
  1204.  
  1205. int
  1206. operattrhc(int f, int n)
  1207. {
  1208.     opcmd = OPOTHER;
  1209.     videoattribute = 0;
  1210.     return vile_op(f,n,attributehyperregion,"Set hypertext command");
  1211. }
  1212.  
  1213. static int
  1214. hyperspray(int (*f)(char *))
  1215. {
  1216.     L_NUM    dlno;
  1217.     int      doff;
  1218.     AREGION *p;
  1219.     int count = 0;
  1220.  
  1221.     (void) bsizes(curbp);        /* attach line numbers to each line */
  1222.  
  1223.     dlno = DOT.l->l_number;
  1224.     doff = DOT.o;
  1225.  
  1226.     for (p = curbp->b_attribs; p != 0; p = p->ar_next) {
  1227.     if (p->ar_hypercmd) {
  1228.         int slno, elno, soff, eoff;
  1229.  
  1230.         slno = p->ar_region.r_orig.l->l_number;
  1231.         elno = p->ar_region.r_end.l->l_number;
  1232.         soff = p->ar_region.r_orig.o;
  1233.         eoff = p->ar_region.r_end.o;
  1234.  
  1235.         if (   ((slno == dlno && doff >= soff) || dlno > slno)
  1236.         && ((elno == dlno && doff <  eoff) || dlno < elno) )
  1237.         {
  1238.         f(p->ar_hypercmd);
  1239.         count++;
  1240.         /* As originally written, there was no break below.
  1241.                 This means that we'd loop over all of the
  1242.             attributes in case there were multiple
  1243.             overlapping attributes with attached hypertext
  1244.             commands.  As cool as this may sound, it is
  1245.             actually of very dubious utility, and an
  1246.             action which removes some or all of the
  1247.             attributes very quickly gets this loop into
  1248.             trouble.  So, if this functionality is really
  1249.             desired, we'll have to either make a copy of
  1250.             the attributes or somehow otherwise determine
  1251.             that they were modified or deleted.  */
  1252.         break;
  1253.         }
  1254.     }
  1255.     }
  1256.     return count;
  1257. }
  1258.  
  1259. static int
  1260. doexechypercmd(char *cmd)
  1261. {
  1262.     return docmd(cmd,TRUE,FALSE,1);
  1263. }
  1264.  
  1265. int
  1266. exechypercmd(int f GCC_UNUSED, int n GCC_UNUSED)
  1267. {
  1268.     int count;
  1269.     count = hyperspray(doexechypercmd);
  1270.     return count != 0;
  1271. }
  1272.  
  1273. static int
  1274. doshowhypercmd(char *cmd)
  1275. {
  1276.     mlforce("%s",cmd);
  1277.     return 1;
  1278. }
  1279.  
  1280. int
  1281. showhypercmd(int f GCC_UNUSED, int n GCC_UNUSED)
  1282. {
  1283.     int count;
  1284.     count = hyperspray(doshowhypercmd);
  1285.     return count != 0;
  1286. }
  1287. #endif
  1288.  
  1289. int
  1290. operattrcaseq(int f, int n)
  1291. {
  1292.       opcmd = OPOTHER;
  1293.       videoattribute = VAUL | VOWN_CTLA;
  1294.       return vile_op(f,n,attribute_cntl_a_sequences,
  1295.               "Attribute ^A sequences");
  1296. }
  1297.  
  1298. int
  1299. attribute_cntl_a_seqs_in_region(REGION *rp, REGIONSHAPE shape)
  1300. {
  1301.     haveregion = rp;
  1302.     DOT =  rp->r_orig;
  1303.     MK = rp->r_end;
  1304.     if (shape == FULLLINE)
  1305.     MK.l = lback(MK.l);
  1306.     regionshape = shape;    /* Not that the following actually cares */
  1307.     return attribute_cntl_a_sequences();
  1308. }
  1309.  
  1310. /*
  1311.  * attribute_cntl_a_sequences can take quite a while when processing a region
  1312.  * with a large number of attributes.  The reason for this is that the number
  1313.  * of marks to check for fixing (from ldelete) increases with each attribute
  1314.  * that is added.  It is not really necessary to check the attributes that
  1315.  * we are adding in attribute_cntl_a_sequences due to the order in which
  1316.  * they are added (i.e, none of them ever need to be fixed up when ldelete
  1317.  * is called from within attribute_cntl_a_sequences).
  1318.  *
  1319.  * It is still necessary to update those attributes which existed (if any)
  1320.  * prior to calling attribute_cntl_a_sequences.
  1321.  *
  1322.  * We define EFFICIENCY_HACK to be 1 if we want to enable the code which
  1323.  * will prevent ldelete from doing unnecessary work.  Note that we are
  1324.  * depending on the fact that attach_attrib() adds new attributes to the
  1325.  * beginning of the list.  It is for this reason that I consider this
  1326.  * code to be somewhat hacky.
  1327.  */
  1328. #define EFFICIENCY_HACK 1
  1329.  
  1330. static int
  1331. attribute_cntl_a_sequences(void)
  1332. {
  1333.     register int c;        /* current char during scan */
  1334.     register LINEPTR pastline;    /* pointer to line just past EOP */
  1335.     C_NUM offset;        /* offset in cur line of place to attribute */
  1336.  
  1337. #if EFFICIENCY_HACK
  1338.     AREGION *orig_attribs;
  1339.     AREGION *new_attribs;
  1340.     orig_attribs = new_attribs = curbp->b_attribs;
  1341. #endif
  1342.  
  1343.     if (!sameline(MK, DOT)) {
  1344.     REGION region;
  1345.     if (getregion(®ion) != TRUE)
  1346.         return FALSE;
  1347.     if (sameline(region.r_orig, MK))
  1348.         swapmark();
  1349.     }
  1350.     pastline = MK.l;
  1351.     if (pastline != win_head(curwp))
  1352.         pastline = lforw(pastline);
  1353.     DOT.o = 0;
  1354.     regionshape = EXACT;
  1355.     while (DOT.l != pastline) {
  1356.     while (DOT.o < llength(DOT.l)) {
  1357.         if (char_at(DOT) == CONTROL_A) {
  1358.         int count = 0;
  1359.         offset = DOT.o+1;
  1360. start_scan:
  1361.         while (offset < llength(DOT.l)) {
  1362.             c = lgetc(DOT.l, offset);
  1363.             if (isDigit(c)) {
  1364.             count = count * 10 + c - '0';
  1365.             offset++;
  1366.             }
  1367.             else
  1368.             break;
  1369.         }
  1370.         if (count == 0)
  1371.             count = 1;
  1372.         videoattribute = VOWN_CTLA;
  1373.         while (offset < llength(DOT.l)) {
  1374.             c = lgetc(DOT.l, offset);
  1375.             switch (c) {
  1376.             case 'C' :
  1377.                 /* We have color. Get color value */
  1378.                 offset++;
  1379.                 c = lgetc(DOT.l, offset);
  1380.                 if (isDigit(c))
  1381.                 videoattribute |= VCOLORATTR(c - '0');
  1382.                 else if ('A' <= c && c <= 'F')
  1383.                 videoattribute |= VCOLORATTR(c - 'A' + 10);
  1384.                 else if ('a' <= c && c <= 'f')
  1385.                 videoattribute |= VCOLORATTR(c - 'a' + 10);
  1386.                 else
  1387.                 offset--; /* Invalid attribute */
  1388.                 break;
  1389.             case 'U' : videoattribute |= VAUL;   break;
  1390.             case 'B' : videoattribute |= VABOLD; break;
  1391.             case 'R' : videoattribute |= VAREV;  break;
  1392.             case 'I' : videoattribute |= VAITAL; break;
  1393. #if OPT_HYPERTEXT
  1394.             case 'H' : {
  1395.                 int save_offset = offset;
  1396.                 offset++;
  1397.                 while (offset < llength(DOT.l)
  1398.                 && lgetc(DOT.l,offset) != 0)
  1399.                 offset++;
  1400.                 if (offset < llength(DOT.l)) {
  1401.                 hypercmd = strmalloc(&DOT.l->l_text[save_offset+1]);
  1402.                 }
  1403.                 else {
  1404.                 /* Bad hypertext string... skip it */
  1405.                 offset = save_offset;
  1406.                 }
  1407.  
  1408.                 break;
  1409.             }
  1410. #endif
  1411.             case ':' : offset++;
  1412.                 if (offset < llength(DOT.l)
  1413.                  && lgetc(DOT.l, offset) == CONTROL_A) {
  1414.                 count = 0;
  1415.                 offset++;
  1416.                 goto start_scan; /* recover from filter-err */
  1417.                 }
  1418.                 /* FALLTHROUGH */
  1419.             default  : goto attribute_found;
  1420.             }
  1421.             offset++;
  1422.         }
  1423. attribute_found:
  1424. #if EFFICIENCY_HACK
  1425.         new_attribs = curbp->b_attribs;
  1426.         curbp->b_attribs = orig_attribs;
  1427.         ldelete((B_COUNT)(offset - DOT.o), FALSE);
  1428.         curbp->b_attribs = new_attribs;
  1429. #else
  1430.         ldelete((B_COUNT)(offset - DOT.o), FALSE);
  1431. #endif
  1432.         MK = DOT;
  1433.         MK.o += count;
  1434.         if (MK.o > llength(DOT.l))
  1435.             MK.o = llength(DOT.l);
  1436.         if (VATTRIB(videoattribute) || hypercmd != 0)
  1437.             (void) attributeregion();
  1438.         }
  1439.         DOT.o++;
  1440.     }
  1441.     DOT.l = lforw(DOT.l);
  1442.     DOT.o = 0;
  1443.     }
  1444.     return TRUE;
  1445. }
  1446. #endif /* OPT_SELECTIONS */
  1447.