home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / vile-src.zip / vile-8.1 / region.c < prev    next >
C/C++ Source or Header  |  1998-07-17  |  22KB  |  1,071 lines

  1. /*
  2.  * The routines in this file
  3.  * deal with the region, that magic space
  4.  * between "." and mark. Some functions are
  5.  * commands. Some functions are just for
  6.  * internal use.
  7.  *
  8.  * $Header: /usr/build/vile/vile/RCS/region.c,v 1.90 1998/07/17 09:54:39 cmorgan Exp $
  9.  *
  10.  */
  11.  
  12. #include    "estruct.h"
  13. #include        "edef.h"
  14.  
  15. typedef    int (*CharProcFunc) (int c);
  16.  
  17. static    int    blankline(void *flagp, int l, int r);
  18. static    int    do_chars_in_line(void *flagp, int ll, int rr);
  19. static    int    do_lines_in_region(int (*linefunc) (REGN_ARGS), void *argp, int convert_cols);
  20. static    int    killrectmaybesave (int save);
  21.  
  22. static CharProcFunc charprocfunc;
  23.  
  24. /*
  25.  * Kill the region. Ask "getregion"
  26.  * to figure out the bounds of the region.
  27.  * Move "." to the start, and kill the characters.
  28.  */
  29. int
  30. killregion(void)
  31. {
  32.     if (regionshape == RECTANGLE)
  33.         return killrectmaybesave(TRUE);
  34.     else
  35.         return killregionmaybesave(TRUE);
  36. }
  37.  
  38. int
  39. killregionmaybesave(int save)
  40. {
  41.     register int    status;
  42.     REGION          region;
  43.  
  44.     if ((status = getregion(®ion)) == TRUE) {
  45.         if (save) {
  46.             kregcirculate(TRUE);
  47.             ksetup();        /* command, so do magic */
  48.             if (regionshape == FULLLINE)
  49.                 kregflag |= KLINES;
  50.         }
  51.         DOT = region.r_orig;
  52.         status = ldelete(region.r_size, save);
  53.         if (save) {
  54.             kdone();
  55.             ukb = 0;
  56.         }
  57.     }
  58. #if OPT_SELECTIONS
  59.     find_release_attr(curbp, ®ion);
  60. #endif
  61.     return status;
  62. }
  63.  
  64. static int
  65. kill_line(void *flagp, int l, int r)
  66. {
  67.     int s;
  68.     int save = *(int *)flagp;
  69.     LINE *lp = DOT.l;
  70.  
  71.     s = detabline((void *)FALSE, 0, 0);
  72.     if (s != TRUE) return s;
  73.  
  74.     DOT.o = l;
  75.  
  76.     if (r > llength(lp))
  77.         r = llength(lp);
  78.  
  79.     if (r > l) {
  80.         s = ldelete((B_COUNT)(r - l), save);
  81.         if (s != TRUE) return s;
  82.     }
  83.  
  84.     if (save)
  85.         kinsert('\n');
  86.  
  87.     if (b_val(curbp,MDTABINSERT))
  88.         s = entabline((void *)TRUE, 0, 0);
  89.  
  90.     DOT.o = l;
  91.     return s;
  92. }
  93.  
  94. static int
  95. killrectmaybesave(int save)
  96. {
  97.     register int    s;
  98.     MARK savedot;
  99.  
  100.     savedot = DOT;
  101.  
  102.     if (save) {
  103.         kregcirculate(TRUE);
  104.         ksetup();
  105.         if (regionshape == FULLLINE) {
  106.             kregflag |= KLINES;
  107.         } else if (regionshape == RECTANGLE) {
  108.             kregflag |= KRECT;
  109.         }
  110.     }
  111.     s = do_lines_in_region(kill_line, (void *)&save, FALSE);
  112.     DOT = savedot;
  113.  
  114.     if (s && do_report(klines+(kchars!=0))) {
  115.         mlwrite("[%d line%s, %d character%s killed]",
  116.             klines, PLURAL(klines),
  117.             kchars, PLURAL(kchars));
  118.     }
  119.     if (save) {
  120.         kdone();
  121.         ukb = 0;
  122.     }
  123.     return (s);
  124. }
  125.  
  126. /*
  127.  * open up a region -- shift the "selected" area of each line by its
  128.  * own length.  most useful for rectangular regions.
  129.  * fill character is space, unless a string is passed in, in which case
  130.  * it is used instead.
  131.  */
  132. /*ARGSUSED*/
  133. static int
  134. open_hole_in_line(void *flagp, int l, int r)
  135. {
  136.         char *string = (char *)flagp;
  137.     int len;
  138.     int s;
  139.     int saveo = DOT.o;
  140.     LINE *lp = DOT.l;
  141.  
  142.     s = detabline((void *)FALSE, 0, 0);
  143.     if (s != TRUE) return s;
  144.  
  145.     if (llength(lp) <= l) {    /* nothing to do if no string */
  146.         if (!string) {
  147.             if (b_val(curbp,MDTABINSERT))
  148.                 s = entabline((void *)TRUE, 0, 0);
  149.             DOT.o = saveo;
  150.             return s;
  151.         } else {
  152.             DOT.o = llength(lp);
  153.             if (l - DOT.o)
  154.             linsert(l - DOT.o, ' ');
  155.         }
  156.     }
  157.     DOT.o = l;
  158.     if (string) {
  159.         len = strlen(string);
  160.         if (len < r - l)
  161.             len = r - l;
  162.     } else {
  163.         len = r - l;
  164.     }
  165.     s =  lstrinsert(string, len );
  166.     if (s != TRUE) return s;
  167.  
  168.     DOT.o = saveo;
  169.     if (b_val(curbp,MDTABINSERT))
  170.         s = entabline((void *)TRUE, 0, 0);
  171.     return s;
  172. }
  173.  
  174. /*
  175.  * open up a region
  176.  */
  177. int
  178. openregion(void)
  179. {
  180.     return do_lines_in_region(open_hole_in_line, (void *)NULL, FALSE);
  181. }
  182.  
  183. /*
  184.  * open up a region, filling it with a supplied string
  185.  * this is pretty simplistic -- could be a lot more clever
  186.  */
  187. int
  188. stringrect(void)
  189. {
  190.     int             s;
  191.     static char     buf[NLINE];
  192.  
  193.     s = mlreply("Rectangle text: ", buf, sizeof(buf) );
  194.     if (s != TRUE)
  195.         return s;
  196.  
  197. /* i couldn't decide at first whether we should be inserting or
  198.     overwriting... this chooses. */
  199. #ifdef insert_the_string
  200.     return do_lines_in_region(open_hole_in_line, (void *)buf, FALSE);
  201. #else /* overwrite the string */
  202.     return do_lines_in_region(blankline, (void *)buf, FALSE);
  203. #endif
  204. }
  205.  
  206. /*
  207.  * insert a shiftwidth at the front of the line
  208.  * don't do it if we're in cmode and the line starts with '#'
  209.  */
  210. /*ARGSUSED*/
  211. static int
  212. shift_right_line(void *flagp GCC_UNUSED, int l GCC_UNUSED, int r GCC_UNUSED)
  213. {
  214.     int s, t;
  215.  
  216.     if (is_empty_line(DOT) || (is_c_mode(curbp) &&
  217.                     llength(DOT.l) > 0 &&
  218.                     char_at(DOT) == '#')) {
  219.         return TRUE;
  220.     }
  221.     s = shiftwid_val(curbp);
  222.     t = curtabval;
  223.     DOT.o = w_left_margin(curwp);
  224.     if (s) {  /* try to just insert tabs if possible */
  225.         if (b_val(curbp,MDTABINSERT) && s >= t && (s % t == 0)) {
  226.             linsert(s/t, '\t');
  227.         } else {
  228.             detabline((void *)TRUE, 0, 0);
  229.             DOT.o = w_left_margin(curwp);
  230.             linsert(s, ' ');
  231.         }
  232.         if (b_val(curbp,MDTABINSERT))
  233.             entabline((void *)TRUE, 0, 0);
  234.     }
  235.     return firstnonwhite(FALSE,1);
  236. }
  237.  
  238. /*
  239.  * shift region right by a tab stop
  240.  * if region is rectangular, "open it up"
  241.  */
  242. int
  243. shiftrregion(void)
  244. {
  245.         if (regionshape == RECTANGLE)
  246.         return do_lines_in_region(open_hole_in_line, (void *)NULL, FALSE);
  247.  
  248.     regionshape = FULLLINE;
  249.     return do_lines_in_region(shift_right_line, (void *)0, FALSE);
  250. }
  251.  
  252. /*
  253.  * delete a shiftwidth-equivalent from the front of the line
  254.  */
  255. /*ARGSUSED*/
  256. static int
  257. shift_left_line(void *flagp GCC_UNUSED, int l GCC_UNUSED, int r GCC_UNUSED)
  258. {
  259.     register int    i;
  260.     register int    lim;
  261.     register int    s;
  262.     register LINE *linep = DOT.l;
  263.  
  264.     if (llength(linep) == 0)
  265.         return TRUE;
  266.  
  267.     s = shiftwid_val(curbp);
  268.  
  269.     detabline((void *)TRUE, 0, 0);
  270.  
  271.     /* examine the line to the end, or the first shiftwidth, whichever
  272.         comes first */
  273.     lim = (s < llength(linep)) ? s : llength(linep);
  274.  
  275.  
  276.     i = 0;
  277.     /* count the leading spaces */
  278.     while (lgetc(linep,i) == ' ' && i < lim)
  279.         i++;
  280.  
  281.     if (i != 0) { /* did we find space/tabs to kill? */
  282.         DOT.o = w_left_margin(curwp);
  283.         if ((s = ldelete((B_COUNT)i,FALSE)) != TRUE)
  284.             return s;
  285.     }
  286.  
  287.     DOT.o = w_left_margin(curwp);
  288.     if (b_val(curbp,MDTABINSERT))
  289.         entabline((void *)TRUE, 0, 0);
  290.     return TRUE;
  291. }
  292.  
  293. /*
  294.  * shift region left by a tab stop
  295.  */
  296. int
  297. shiftlregion(void)
  298. {
  299.         if (regionshape == RECTANGLE)
  300.         return killrectmaybesave(FALSE);
  301.  
  302.     regionshape = FULLLINE;
  303.     return do_lines_in_region(shift_left_line, (void *)0, FALSE);
  304. }
  305.  
  306. /*
  307.  * change all tabs in the line to the right number of spaces.
  308.  * leadingonly says only do leading whitespace
  309.  */
  310. /*ARGSUSED*/
  311. int
  312. detabline(void *flagp GCC_UNUSED, int l GCC_UNUSED, int r GCC_UNUSED)
  313. {
  314.     register int    s;
  315.     register int    c;
  316.     int    ocol;
  317.     long leadingonly = (long)flagp;
  318.     LINE *lp = DOT.l;
  319.  
  320.     if (llength(lp) == 0)
  321.         return TRUE;
  322.  
  323.     ocol = getccol(FALSE);
  324.  
  325.     DOT.o = 0;
  326.  
  327.     /* detab the entire current line */
  328.     while (DOT.o < llength(lp)) {
  329.         c = char_at(DOT);
  330.         if (leadingonly && !isSpace(c))
  331.             break;
  332.         /* if we have a tab */
  333.         if (c == '\t') {
  334.             if ((s = ldelete(1L, FALSE)) != TRUE
  335.             ||  (s = insspace(TRUE, curtabval - (DOT.o % curtabval) )) != TRUE)
  336.                 return s;
  337.         }
  338.         DOT.o++;
  339.     }
  340.     (void)gocol(ocol);
  341.     return TRUE;
  342. }
  343.  
  344.  
  345. /*
  346.  * change all tabs in the region to the right number of spaces
  347.  */
  348. #if OPT_AEDIT
  349. int
  350. detab_region(void)
  351. {
  352.     regionshape = FULLLINE;
  353.     return do_lines_in_region(detabline,(void *)FALSE, FALSE);
  354. }
  355. #endif
  356.  
  357. /*
  358.  * convert all appropriate spaces in the line to tab characters.
  359.  * leadingonly says only do leading whitespace
  360.  */
  361. /*ARGSUSED*/
  362. int
  363. entabline(void *flagp GCC_UNUSED, int l GCC_UNUSED, int r GCC_UNUSED)
  364. {
  365.     register int fspace;    /* pointer to first space if in a run */
  366.     register int ccol;    /* current cursor column */
  367.     register char cchar;    /* current character */
  368.     int    ocol;
  369.     long leadingonly = (long)flagp;
  370.     LINE *lp = DOT.l;
  371.  
  372.     if (llength(lp) == 0)
  373.         return TRUE;
  374.  
  375.     ocol = getccol(FALSE);
  376.  
  377.     /* entab the current line */
  378.     /* would this have been easier if it had started at
  379.         the _end_ of the line, rather than the beginning?  -pgf */
  380.     fspace = -1;
  381.     ccol = 0;
  382.  
  383.     detabline(flagp, 0, 0);    /* get rid of possible existing tabs */
  384.     DOT.o = 0;
  385.     for_ever {
  386.         /* see if it is time to compress */
  387.         if ((fspace >= 0) && (nextab(fspace) <= ccol)) {
  388.             if (ccol - fspace < 2) {
  389.                 fspace = -1;
  390.             } else {
  391.                 backchar(TRUE, ccol - fspace);
  392.                 (void)ldelete((B_COUNT)(ccol - fspace), FALSE);
  393.                 linsert(1, '\t');
  394.                 fspace = -1;
  395.             }
  396.         }
  397.  
  398.         if (DOT.o >= llength(lp))
  399.             break;
  400.  
  401.         /* get the current character */
  402.         cchar = char_at(DOT);
  403.  
  404.         if (cchar == ' ') { /* a space...compress? */
  405.             if (fspace == -1)
  406.                 fspace = ccol;
  407.         } else {
  408.             if (leadingonly)
  409.                 break;
  410.             fspace = -1;
  411.         }
  412.         ccol++;
  413.         DOT.o++;
  414.     }
  415.     (void)gocol(ocol);
  416.     return TRUE;
  417. }
  418.  
  419. /*
  420.  * convert all appropriate spaces in the region to tab characters
  421.  */
  422. #if OPT_AEDIT
  423. int
  424. entab_region(void)
  425. {
  426.     regionshape = FULLLINE;
  427.     return do_lines_in_region(entabline,(void *)FALSE, FALSE);
  428. }
  429. #endif
  430.  
  431. /* trim trailing whitespace from a line.  dot is preserved if possible. */
  432. /* (dot is even preserved if it was sitting on the newline) */
  433. /*ARGSUSED*/
  434. int
  435. trimline(void *flag GCC_UNUSED, int l GCC_UNUSED, int r GCC_UNUSED)
  436. {
  437.     register int off;
  438.     register LINE *lp;
  439.     int odoto, s;
  440.     int delcnt, was_at_eol;
  441.  
  442.     lp = DOT.l;
  443.  
  444.     if (llength(lp) == 0)
  445.         return TRUE;
  446.  
  447.     /* may return -1 if line is all whitespace.  but
  448.         that's okay, since the math still works. */
  449.     off = lastchar(lp);
  450.  
  451.     delcnt = llength(lp) - (off + 1);
  452.     if (!delcnt)
  453.         return TRUE;
  454.  
  455.     odoto = DOT.o;
  456.     was_at_eol = (odoto == llength(lp));
  457.  
  458.     DOT.o = off + 1;
  459.     s = ldelete((B_COUNT)delcnt,FALSE);
  460.  
  461.     if (odoto > off) {    /* do we need to back up? */
  462.         odoto = llength(lp);
  463.         if (!was_at_eol)
  464.             odoto--; /* usually we want the last char on line */
  465.     }
  466.  
  467.     if (odoto < 0)
  468.         DOT.o = 0;
  469.     else
  470.         DOT.o = odoto;
  471.     return s;
  472. }
  473.  
  474. /*
  475.  * trim trailing whitespace from a region
  476.  */
  477. #if OPT_AEDIT
  478. int
  479. trim_region(void)
  480. {
  481.     regionshape = FULLLINE;
  482.     return do_lines_in_region(trimline, (void *)0, FALSE);
  483. }
  484. #endif
  485.  
  486. /* turn line, or part, to whitespace */
  487. /*ARGSUSED*/
  488. static int
  489. blankline(void *flagp, int l, int r)
  490. {
  491.         char *string = (char *)flagp;
  492.     int len;
  493.     int s = TRUE;
  494.     int saveo;
  495.     LINE *lp = DOT.l;
  496.  
  497.     saveo = l;
  498.  
  499.     /* if the shape is rectangular, then l and r are columns, not
  500.         offsets */
  501.     if (regionshape == RECTANGLE) {
  502.         s = detabline((void *)FALSE, 0, 0);
  503.         if (s != TRUE)
  504.             return s;
  505.     }
  506.  
  507.     if (llength(lp) <= l) {    /* nothing to do if no string */
  508.         if (!string) {
  509.             if (regionshape == RECTANGLE && b_val(curbp,MDTABINSERT))
  510.                 s = entabline((void *)TRUE, 0, 0);
  511.             DOT.o = saveo;
  512.             return s;
  513.         } else {
  514.             DOT.o = llength(lp);
  515.             if (l - DOT.o)
  516.             linsert(l - DOT.o, ' ');
  517.         }
  518.     }
  519.  
  520.     DOT.o = l;
  521.  
  522.     if (llength(lp) <= r) {
  523.             /* then the rect doesn't extend to the end of line */
  524.         ldelete((B_COUNT)(llength(lp) - l), FALSE);
  525.  
  526.         /* so there's nothing beyond the rect, so insert at
  527.             most r-l chars of the string, or nothing */
  528.         if (string) {
  529.             len = strlen(string);
  530.             if (len > r - l)
  531.                 len = r - l;
  532.         } else {
  533.             len = 0;
  534.         }
  535.     } else {
  536.             /* the line goes on, so delete and reinsert exactly */
  537.         ldelete((B_COUNT)(r - l), FALSE);
  538.             len = r - l;
  539.     }
  540.  
  541.     s = lstrinsert(string, len);
  542.     if (s != TRUE) return s;
  543.  
  544.         if (regionshape == RECTANGLE && b_val(curbp,MDTABINSERT))
  545.         s = entabline((void *)TRUE, 0, 0);
  546.  
  547.     return s;
  548. }
  549.  
  550. /*
  551.  * Copy all of the characters in the
  552.  * region to the kill buffer. Don't move dot
  553.  * at all. This is a bit like a kill region followed
  554.  * by a yank.
  555.  */
  556. static int
  557. _yankchar(int c)
  558. {
  559.     kinsert(c);
  560.     /* FIXX check return value, longjmp back to yank_line */
  561.     return -1;
  562. }
  563.  
  564. /*ARGSUSED*/
  565. static int
  566. yank_line(void *flagp GCC_UNUSED, int l, int r)
  567. {
  568.     int s;
  569.     charprocfunc = _yankchar;
  570.     s = do_chars_in_line((void  *)NULL, l, r);
  571.     if (s) {
  572.         if (r == llength(DOT.l) || regionshape == RECTANGLE) {
  573.         /* we don't necessarily want to insert the last newline
  574.             in a region, so we delay it */
  575.         s = kinsertlater('\n');
  576.         }
  577.         else if (r > llength(DOT.l)) {
  578.         /* This can happen when selecting with the mouse. */
  579.         kinsert('\n');
  580.         }
  581.     }
  582.     return s;
  583. }
  584.  
  585. int
  586. yankregion(void)
  587. {
  588.     register int    s;
  589.  
  590.     kregcirculate(TRUE);
  591.     ksetup();
  592.     if (regionshape == FULLLINE) {
  593.         kregflag |= KLINES|KYANK;
  594.     } else if (regionshape == RECTANGLE) {
  595.         kregflag |= KRECT|KYANK;
  596.     }
  597.     s = do_lines_in_region(yank_line, (void *)0, TRUE);
  598.     if (s && do_report(klines+(kchars!=0))) {
  599.         mlwrite("[%d line%s, %d character%s yanked]",
  600.             klines, PLURAL(klines),
  601.             kchars, PLURAL(kchars));
  602.     }
  603.     kdone();
  604.     ukb = 0;
  605.     return (s);
  606. }
  607.  
  608.  
  609. #if NEEDED
  610. int
  611. _blankchar(int c)
  612. {
  613.     if (!isSpace(c))
  614.         return ' ';
  615.     return -1;
  616. }
  617. #endif
  618.  
  619. static int
  620. _to_lower(int c)
  621. {
  622.     if (isUpper(c))
  623.         return c ^ DIFCASE;
  624.     return -1;
  625. }
  626.  
  627. static int
  628. _to_upper(int c)
  629. {
  630.     if (isLower(c))
  631.         return c ^ DIFCASE;
  632.     return -1;
  633. }
  634.  
  635. static int
  636. _to_caseflip(int c)
  637. {
  638.     if (isAlpha(c))
  639.         return c ^ DIFCASE;
  640.     return -1;
  641. }
  642.  
  643. /*
  644.  * turn region to whitespace
  645.  */
  646. #if OPT_AEDIT
  647. int
  648. blank_region(void)
  649. {
  650.     return do_lines_in_region(blankline, (void *)NULL, FALSE);
  651. }
  652. #endif
  653.  
  654. int
  655. flipregion(void)
  656. {
  657.     charprocfunc = _to_caseflip;
  658.     return do_lines_in_region(do_chars_in_line,(void *)NULL, TRUE);
  659. }
  660.  
  661. int
  662. lowerregion(void)
  663. {
  664.     charprocfunc = _to_lower;
  665.     return do_lines_in_region(do_chars_in_line,(void *)NULL, TRUE);
  666. }
  667.  
  668. int
  669. upperregion(void)
  670. {
  671.     charprocfunc = _to_upper;
  672.     return do_lines_in_region(do_chars_in_line,(void *)NULL, TRUE);
  673. }
  674.  
  675. #if OPT_ENCRYPT
  676. static int
  677. crypt_char(int c)
  678. {
  679.     char nc = c;
  680.     ue_crypt(&nc, 1);
  681.     if (nc != c)
  682.         return nc;
  683.     return -1;
  684. }
  685.  
  686. int
  687. cryptregion(void)
  688. {
  689.     int s;
  690.     char    temp[NPAT];
  691.     /* make a key if we don't have one */
  692.     if (curbp->b_key[0] == EOS) {
  693.         s = ue_makekey(curbp->b_key, sizeof(curbp->b_key));
  694.         if (s != TRUE)
  695.             return (s == FALSE);
  696.     }
  697.     (void)strcpy(temp, curbp->b_key);
  698.     ue_crypt((char *)0, 0);
  699.     ue_crypt(temp, strlen(temp));
  700.     charprocfunc = crypt_char;
  701.     return do_lines_in_region(do_chars_in_line,(void *)NULL, TRUE);
  702. }
  703. #endif
  704.  
  705. #if NEEDED
  706. /* this walks a region, char by char, and invokes a funcion for
  707.      each.  it does _not_ know about rectangles, which is why it is
  708.     probably obsolete -- we can do_lines_in_region/do_chars_in_line
  709.     to get the same effect*/
  710. int
  711. charprocreg(int (*func)(int))
  712. {
  713.     MARK        m;
  714.     register int    c,nc;
  715.     register int    status;
  716.     REGION          region;
  717.     int         changed = 0;
  718.  
  719.     if ((status = getregion(®ion)) == TRUE) {
  720.         m = region.r_orig;
  721.         while (region.r_size-- > 0) {
  722.             if (is_at_end_of_line(m)) {
  723.                 m.l = lforw(m.l);
  724.                 m.o = w_left_margin(curwp);
  725.             } else {
  726.                 c = char_at(m);
  727.                 nc = (func)(c);
  728.                 if (nc != -1) {
  729.                     copy_for_undo(m.l);
  730.                     put_char_at(m, nc);
  731.                     changed++;
  732.                 }
  733.                 ++m.o;
  734.             }
  735.         }
  736.     }
  737.     if (changed)
  738.         chg_buff(curbp, WFHARD);
  739.     return (status);
  740. }
  741. #endif
  742.  
  743. /* finish filling in the left/right column info for a rectangular region */
  744. static void
  745. set_rect_columns(register REGION *rp)
  746. {
  747.     if (regionshape == RECTANGLE) {
  748.         MARK    lc, rc;
  749.  
  750.         lc = (rp->r_orig.o < rp->r_end.o) ? rp->r_orig : rp->r_end;
  751.         rc = (rp->r_orig.o > rp->r_end.o) ? rp->r_orig : rp->r_end;
  752.  
  753.         /* convert to columns */
  754.         rp->r_leftcol = getcol(lc, FALSE);
  755.         rp->r_rightcol = getcol(rc, FALSE) + 1;
  756.     }
  757. }
  758.  
  759.  
  760. /*
  761.  * This routine figures out the
  762.  * bounds of the region in the current window, and
  763.  * fills in the fields of the "REGION" structure pointed
  764.  * to by "rp". Because the dot and mark are usually very
  765.  * close together, we scan outward from dot looking for
  766.  * mark. This should save time. Return a standard code.
  767.  */
  768.  
  769. REGION *haveregion;
  770.  
  771. int
  772. getregion(register REGION *rp)
  773. {
  774.     register LINE   *flp;
  775.     register LINE   *blp;
  776.     B_COUNT fsize;
  777.     B_COUNT bsize;
  778.  
  779.     if (haveregion) {
  780.         *rp = *haveregion;
  781.         haveregion = NULL;
  782.         return TRUE;
  783.     }
  784.  
  785. #if OPT_SELECTIONS
  786.     rp->r_attr_id = (unsigned short) assign_attr_id();
  787. #endif
  788.  
  789.     if (MK.l == NULL) {
  790.         mlforce("BUG: getregion: no mark set in this window");
  791.         return FALSE;
  792.     }
  793.  
  794. #define line_length(lp) (llength(lp)+1)    /* length counting newline */
  795.     if (sameline(DOT, MK)) {
  796.         rp->r_orig =
  797.         rp->r_end  = DOT;
  798.         if (regionshape == FULLLINE) {
  799.             rp->r_orig.o =
  800.             rp->r_end.o  = w_left_margin(curwp);
  801.             rp->r_end.l  = lforw(DOT.l);
  802.             rp->r_size   = (B_COUNT)(line_length(DOT.l) - w_left_margin(curwp));
  803.         } else {
  804.             if (DOT.o < MK.o) {
  805.                 rp->r_orig.o = rp->r_leftcol = DOT.o;
  806.                 rp->r_end.o  = rp->r_rightcol = MK.o;
  807.             } else {
  808.                 rp->r_orig.o = rp->r_leftcol = MK.o;
  809.                 rp->r_end.o  = rp->r_rightcol = DOT.o;
  810.             }
  811.             rp->r_size = rp->r_end.o - rp->r_orig.o;
  812.             set_rect_columns(rp);
  813.         }
  814.         return TRUE;
  815.     }
  816.  
  817. #if !SMALLER
  818.     if (b_is_counted(curbp)) { /* we have valid line numbers */
  819.         register LINE   *flp_start;
  820.         L_NUM dno, mno;
  821.         dno = line_no(curbp, DOT.l);
  822.         mno = line_no(curbp, MK.l);
  823.         if (mno > dno) {
  824.             flp = DOT.l;
  825.             blp = MK.l;
  826.             rp->r_orig = DOT;
  827.             rp->r_end  = MK;
  828.         } else {
  829.             flp = MK.l;
  830.             blp = DOT.l;
  831.             rp->r_orig = MK;
  832.             rp->r_end  = DOT;
  833.         }
  834.         fsize = (B_COUNT)(line_length(flp) -
  835.                     ((regionshape == FULLLINE) ?
  836.                     w_left_margin(curwp) : rp->r_orig.o));
  837.         flp_start = flp;
  838.         while (flp != blp) {
  839.             flp = lforw(flp);
  840.             if (flp != buf_head(curbp) && flp != flp_start) {
  841.                 fsize += line_length(flp) - w_left_margin(curwp);
  842.             } else if (flp != blp) {
  843.                 mlwrite ("BUG: hit buf end in getregion");
  844.                 return FALSE;
  845.             }
  846.  
  847.             if (flp == blp) {
  848.                 if (regionshape == FULLLINE) {
  849.                     rp->r_orig.o =
  850.                     rp->r_end.o  = w_left_margin(curwp);
  851.                     rp->r_end.l  = lforw(rp->r_end.l);
  852.                 } else {
  853.                     fsize -=
  854.                         (line_length(flp) - rp->r_end.o);
  855.                     set_rect_columns(rp);
  856.                 }
  857.                 rp->r_size = fsize;
  858.                 return TRUE;
  859.             }
  860.         }
  861.     } else
  862. #endif /* !SMALLER */
  863.     {
  864.         blp = DOT.l;
  865.         flp = DOT.l;
  866.         if (regionshape == FULLLINE) {
  867.             bsize = fsize =
  868.             (B_COUNT)(line_length(blp) - w_left_margin(curwp));
  869.         } else {
  870.             bsize = (B_COUNT)(DOT.o - w_left_margin(curwp));
  871.             fsize = (B_COUNT)(line_length(flp) - DOT.o);
  872.         }
  873.         while ((flp != buf_head(curbp)) ||
  874.                 (lback(blp) != buf_head(curbp))) {
  875.             if (flp != buf_head(curbp)) {
  876.             flp = lforw(flp);
  877.             if (flp != buf_head(curbp))
  878.                 fsize += line_length(flp) - w_left_margin(curwp);
  879.             if (flp == MK.l) {
  880.                 rp->r_orig = DOT;
  881.                 rp->r_end  = MK;
  882.                 if (regionshape == FULLLINE) {
  883.                     rp->r_orig.o =
  884.                     rp->r_end.o  = w_left_margin(curwp);
  885.                     rp->r_end.l  = lforw(rp->r_end.l);
  886.                 } else {
  887.                     fsize -= (line_length(flp) - MK.o);
  888.                     set_rect_columns(rp);
  889.                 }
  890.                 rp->r_size = fsize;
  891.                 return TRUE;
  892.             }
  893.             }
  894.             if (lback(blp) != buf_head(curbp)) {
  895.             blp = lback(blp);
  896.             bsize += line_length(blp) - w_left_margin(curwp);
  897.             if (blp == MK.l) {
  898.                 rp->r_orig = MK;
  899.                 rp->r_end  = DOT;
  900.                 if (regionshape == FULLLINE) {
  901.                     rp->r_orig.o =
  902.                     rp->r_end.o  = w_left_margin(curwp);
  903.                     rp->r_end.l  = lforw(rp->r_end.l);
  904.                 } else {
  905.                     bsize -= (MK.o - w_left_margin(curwp));
  906.                     set_rect_columns(rp);
  907.                 }
  908.                 rp->r_size = bsize;
  909.                 return TRUE;
  910.             }
  911.             }
  912.         }
  913.     }
  914.     mlforce("BUG: lost mark");
  915.     return FALSE;
  916. }
  917.  
  918. int
  919. get_fl_region(REGION *rp)
  920. {
  921.     int    status;
  922.  
  923.     regionshape = FULLLINE;
  924.     status = getregion(rp);
  925.     regionshape = EXACT;
  926.  
  927.     return status;
  928. }
  929.  
  930.  
  931. /* the (*linefunc)() routine that this calls _must_ be prepared to
  932.     to get empty lines passed to it from this routine. */
  933. static int
  934. do_lines_in_region(
  935. int (*linefunc) (REGN_ARGS),
  936. void *argp,
  937. int convert_cols)    /* if rectangle, convert columns to offsets */
  938. {
  939.     register LINE   *linep;
  940.     register int    status;
  941.     REGION          region;
  942.     C_NUM        l, r;
  943.  
  944.     if ((status=getregion(®ion)) == TRUE) {
  945.  
  946.         /* for each line in the region, ... */
  947.         linep = region.r_orig.l;
  948.         for_ever {
  949.             /* move through the region... */
  950.             /* it's important that the linefunc get called
  951.                 for every line, even if blank, since it
  952.                 may want to keep track of newlines, for
  953.                 instance */
  954.             DOT.l = linep;
  955.             DOT.o = w_left_margin(curwp);
  956.             if (regionshape == RECTANGLE) {
  957.                 if (convert_cols) {
  958.                 C_NUM reached;
  959.                 l = getoff(region.r_leftcol, &reached);
  960.                 if (l < 0) l = -l + llength(linep);
  961.                 r = getoff(region.r_rightcol, &reached);
  962.                 if (r < 0) r = -r + llength(linep);
  963.                 if (reached > region.r_rightcol) /* a tab? */
  964.                     reached = region.r_rightcol;
  965.                 } else {
  966.                 l = region.r_leftcol;
  967.                 r = region.r_rightcol;
  968.                 }
  969.             } else {
  970.                 l =  w_left_margin(curwp);
  971.                 r = llength(DOT.l);
  972.                 if (sameline(region.r_orig, DOT))
  973.                     l = region.r_orig.o;
  974.                 if (sameline(region.r_end, DOT)) {
  975.                     r = region.r_end.o;
  976.                     /* if we're on the end-of-
  977.                      * region, in col 0, we're
  978.                      * done. we don't want to
  979.                      * call teh line function
  980.                      * for the empty case
  981.                      */
  982.                     if (r == 0)
  983.                         break;
  984.                 }
  985.             }
  986.  
  987.             /* ...and process each line */
  988.             if ((status = (*linefunc)(argp,l,r)) != TRUE) {
  989.                 return status;
  990.             }
  991.  
  992.             if (linep == region.r_end.l)
  993.                 break;
  994.  
  995.             linep = lforw(linep);
  996.  
  997.             /*
  998.              * If this happened to be a full-line region, then
  999.              * we've bumped the end-region pointer to the next line
  1000.              * so we won't run into problems with the width.  But
  1001.              * we don't really want to _process_ the end-region
  1002.              * line, so check here also. (Leave the above check
  1003.              * alone just in case the buffer is empty).
  1004.              */
  1005.             if (regionshape == FULLLINE
  1006.              && linep == region.r_end.l)
  1007.                 break;
  1008.  
  1009.         }
  1010.         if (regionshape == FULLLINE) {
  1011.             (void)kinsertlater(-1);
  1012.             (void)firstnonwhite(FALSE,1);
  1013.         }
  1014.     }
  1015. #if OPT_SELECTIONS
  1016.     if (linefunc == kill_line) /* yuck.  it's the only one that deletes */
  1017.         find_release_attr(curbp, ®ion);
  1018. #endif
  1019.     return status;
  1020. }
  1021.  
  1022.  
  1023. /* walk through the characters in a line, applying a function.  the
  1024.     line is marked changed if the function returns other than -1.
  1025.     if it returns other than -1, then the char is replaced with
  1026.     that value.
  1027.     the ll and rr args are OFFSETS, so if you use this routine with
  1028.         do_lines_in_region, tell it to CONVERT columns to offsets. */
  1029. /*ARGSUSED*/
  1030. static int
  1031. do_chars_in_line(
  1032. void    *flagp GCC_UNUSED,
  1033. int    ll, int    rr)        /* offsets of of chars to be processed */
  1034. {
  1035.     register int    c,nc;
  1036.     int         changed = 0;
  1037.     register LINE *lp;
  1038.     int i;
  1039.  
  1040.     lp = DOT.l;
  1041.  
  1042.  
  1043.     if (llength(lp) < ll)
  1044.         return TRUE;
  1045.  
  1046.     DOT.o = ll;
  1047.     if (rr > llength(lp))
  1048.         rr = llength(lp);
  1049.  
  1050.     for (i = ll; i < rr; i++) {
  1051.         c = lgetc(lp,i);
  1052.         nc = (charprocfunc)(c);
  1053.         if (nc != -1) {
  1054.             copy_for_undo(lp);
  1055.             lputc(lp,i,(char)nc);
  1056.             changed++;
  1057.         }
  1058.     }
  1059.     if (changed)
  1060.         chg_buff(curbp, WFHARD);
  1061.     return TRUE;
  1062. }
  1063.  
  1064.  
  1065. /* Helper function for vile commands that must access do_lines_in_region(). */
  1066. DORGNLINES
  1067. get_do_lines_rgn(void)
  1068. {
  1069.     return (do_lines_in_region);
  1070. }
  1071.