home *** CD-ROM | disk | FTP | other *** search
/ Fresh Fish 5 / FreshFish_July-August1994.bin / bbs / util / jade-3.0.lha / Jade / src / edit.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-04-19  |  21.9 KB  |  981 lines

  1. /* edit.c -- Editing buffers
  2.    Copyright (C) 1993, 1994 John Harper <jsh@ukc.ac.uk>
  3.  
  4. This file is part of Jade.
  5.  
  6. Jade is free software; you can redistribute it and/or modify it
  7. under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 2, or (at your option)
  9. any later version.
  10.  
  11. Jade is distributed in the hope that it will be useful, but
  12. WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with Jade; see the file COPYING.    If not, write to
  18. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  19.  
  20. #include "jade.h"
  21. #include "jade_protos.h"
  22.  
  23. #include <string.h>
  24. #include <ctype.h>
  25.  
  26. _PR bool clearlinelist(TX *);
  27. _PR void killlinelist(TX *);
  28. _PR LINE *resizelinelist(TX *, long, long);
  29. _PR bool insertgap(TX *, long, const POS *);
  30. _PR bool insertstr(TX *, const u_char *, POS *);
  31. _PR bool insertstrn(TX *, const u_char *, long, POS *);
  32. _PR bool insertstring(TX *, const u_char *, long, POS *);
  33. _PR bool rectinsertstring(TX *, const u_char *, POS *);
  34. _PR bool deletechars(TX *, const POS *, long);
  35. _PR bool deletelines(TX *, long, long);
  36. _PR bool deletesection(TX *, POS *, POS *);
  37. _PR bool rectdeletesection(TX *, POS *, POS *);
  38. _PR bool splitline(TX *, POS *);
  39. static bool joinlines(TX *, POS *);
  40. _PR bool padpos(TX *, POS *);
  41. _PR bool padcursor(VW *);
  42. _PR void orderpos(POS *, POS *);
  43. _PR void orderrect(POS *, POS *, POS *, POS *);
  44. _PR bool checksect(TX *, POS *, POS *);
  45. _PR void checkpos(TX *, POS *);
  46. _PR bool checkline(TX *, POS *);
  47. _PR long sectionlength(TX *, POS *, POS *);
  48. _PR void copysection(TX *, POS *, POS *, u_char *);
  49. _PR long rectsectionlength(TX *, POS *, POS *);
  50. _PR void rectcopysection(TX *, POS *, POS *, u_char *);
  51. _PR bool colcopysect(TX *, long *, u_char *, long);
  52. _PR bool posinblock(VW *, long, long);
  53. _PR bool cursinblock(VW *);
  54. _PR bool pageinblock(VW *);
  55. _PR short lineinblock(VW *, long);
  56. _PR void orderblock(VW *);
  57. _PR void setblockrefresh(VW *);
  58. _PR bool readonly(TX *);
  59.  
  60. #define ALLOCLL(n)   mymalloc(sizeof(LINE) * (n))
  61. #define FREELL(l)    myfree(l)
  62.  
  63. /*
  64.  * This copies a line list (or part of one).
  65.  * d = destination
  66.  * s = source
  67.  * n = number of LINE's to copy.
  68.  */
  69. #define MOVLL(d,s,n) memcpy((d), (s), (n) * sizeof(LINE))
  70. /*
  71.  * void
  72.  * MOVLL(LINE *dst, LINE *src, long number)
  73.  * {
  74.  *     memcpy(dst, src, number * sizeof(LINE));
  75.  * }
  76.  */
  77.  
  78. /*
  79.  * Makes file empty (null string in first line)
  80.  */
  81. bool
  82. clearlinelist(TX *tx)
  83. {
  84.     if(tx->tx_Lines)
  85.     killlinelist(tx);
  86.     tx->tx_Lines = ALLOCLL(1);
  87.     if(tx->tx_Lines)
  88.     {
  89.     tx->tx_Lines[0].ln_Line = mystrdupn("", 0);
  90.     if(tx->tx_Lines[0].ln_Line)
  91.         tx->tx_Lines[0].ln_Strlen = 1;
  92.     else
  93.         tx->tx_Lines[0].ln_Strlen = 0;
  94.     tx->tx_NumLines = 1;
  95.     return(TRUE);
  96.     }
  97.     return(FALSE);
  98. }
  99.  
  100. /*
  101.  * deallocates all lines and their list
  102.  */
  103. void
  104. killlinelist(TX *tx)
  105. {
  106.     if(tx->tx_Lines)
  107.     {
  108.     LINE *line;
  109.     long i;
  110.     for(i = 0, line = tx->tx_Lines; i < tx->tx_NumLines; i++, line++)
  111.     {
  112.         if(line->ln_Strlen)
  113.         mystrfree(line->ln_Line);
  114.     }
  115.     FREELL(tx->tx_Lines);
  116.     tx->tx_Lines = NULL;
  117.     tx->tx_NumLines = 0;
  118.     }
  119. }
  120.  
  121. /*
  122.  * deallocates some lines (but not the list)
  123.  */
  124. static void
  125. killsomelines(TX *tx, long start, long number)
  126. {
  127.     LINE *line = tx->tx_Lines + start;
  128.     long i;
  129.     for(i = 0; i < number; i++, line++)
  130.     {
  131.     if(line->ln_Strlen)
  132.     {
  133.         mystrfree(line->ln_Line);
  134.         line->ln_Strlen = 0;
  135.         line->ln_Line = NULL;
  136.     }
  137.     }
  138. }
  139.  
  140. /*
  141.  * Creates blank entries or removes existing lines starting from line 'where'
  142.  * 'change' is the number of lines to insert, negative numbers mean delete
  143.  * that number of lines starting at the cursor line.
  144.  * If lines are deleted the actual text is also deleted.
  145.  * NOTE: You can't have a line list of zero lines.
  146.  */
  147. LINE *
  148. resizelinelist(TX *tx, long change, long where)
  149. {
  150.     LINE *newlines;
  151.     long newsize = tx->tx_NumLines + change;
  152.     if((newsize > 0) && (newlines = ALLOCLL(newsize)))
  153.     {
  154.     if(tx->tx_Lines)
  155.     {
  156.         if(change > 0)
  157.         {
  158.         MOVLL(newlines, tx->tx_Lines, where);
  159.         MOVLL(newlines + where + change, tx->tx_Lines + where, tx->tx_NumLines - where);
  160.         }
  161.         else
  162.         {
  163.         MOVLL(newlines, tx->tx_Lines, where);
  164.         MOVLL(newlines + where, tx->tx_Lines + where - change, tx->tx_NumLines - where + change);
  165.         killsomelines(tx, where, -change);
  166.         }
  167.         FREELL(tx->tx_Lines);
  168.     }
  169.     if(change > 0)
  170.         bzero(newlines + where, change * sizeof(LINE));
  171.     tx->tx_Lines = newlines;
  172.     tx->tx_NumLines = newsize;
  173.     return(newlines);
  174.     }
  175.     return(FALSE);
  176. }
  177.  
  178. #if 0
  179. /*
  180.  * Pastes a line into the current view at line num.
  181.  * a LINE should have been made if it is wanted.
  182.  * text is not copied, it should have been mymalloc()'ed (or savestring()'ed)
  183.  */
  184. bool
  185. stuffline(TX *tx, u_char *text, long lineNum)
  186. {
  187.     LINE *line = tx->tx_Lines + lineNum;
  188.     if(line->ln_Strlen)
  189.     mystrfree(line->ln_Line);
  190.     line->ln_Strlen = strlen(text) + 1;
  191.     line->ln_Line = text;
  192.     return(TRUE);
  193. }
  194. #endif
  195.  
  196. /*
  197.  * Inserts some `space' at pos. The gap will be filled with random garbage.
  198.  * pos is *not* altered.
  199.  */
  200. bool
  201. insertgap(TX *tx, long len, const POS *pos)
  202. {
  203.     LINE *line = tx->tx_Lines + pos->pos_Line;
  204.     u_char *newline = mystralloc(len + line->ln_Strlen);
  205.     if(newline)
  206.     {
  207.     if(line->ln_Strlen)
  208.     {
  209.         memcpy(newline, line->ln_Line, pos->pos_Col);
  210.         strcpy(newline + pos->pos_Col + len, line->ln_Line + pos->pos_Col);
  211.         mystrfree(line->ln_Line);
  212.     }
  213.     else
  214.         newline[len] = 0;
  215.     line->ln_Line = newline;
  216.     line->ln_Strlen += len;
  217.     keepposaddx(tx, len, pos->pos_Col, pos->pos_Line);
  218.     return(TRUE);
  219.     }
  220.     return(FALSE);
  221. }
  222.  
  223. /*
  224.  * Inserts a string into the current line at the cursor pos
  225.  *
  226.  * IMPORTANT: For any of the next functions which insert text the pos
  227.  * argument must not be one which will be fiddled with by the keeppos*()
  228.  * functions (specifically a direct pointer to vw_CursorPos).
  229.  */
  230. bool
  231. insertstr(TX *tx, const u_char *text, POS *pos)
  232. {
  233.     return(insertstrn(tx, text, strlen(text), pos));
  234. }
  235.  
  236. bool
  237. insertstrn(TX *tx, const u_char *text, long textLen, POS *pos)
  238. {
  239.     LINE *line = tx->tx_Lines + pos->pos_Line;
  240.     if(insertgap(tx, textLen, pos))
  241.     {
  242.     memcpy(line->ln_Line + pos->pos_Col, text, textLen);
  243.     pos->pos_Col += textLen;
  244.     return(TRUE);
  245.     }
  246.     return(FALSE);
  247. }
  248.  
  249. /*
  250.  * Inserts a null teminated string, this routine acts on any '\n' or '\t'
  251.  * characters that it finds.
  252.  * I expect that this routine will be incredibly slow.
  253.  */
  254. bool
  255. insertstring(TX *tx, const u_char *text, long tabSize, POS *pos)
  256. {
  257. #define INSERT_BUF_LEN 128
  258.     u_char *buff = mystralloc(INSERT_BUF_LEN);
  259.     if(buff)
  260.     {
  261.     const u_char *str = text;
  262.     POS orig = *pos;
  263.     while(*str)
  264.     {
  265.         u_char c;
  266.         long i = 0;
  267.         long startx = pos->pos_Col;
  268.         while((c = *str++) && (c != '\n') && (i < INSERT_BUF_LEN))
  269.         {
  270.         if(c == '\t')
  271.         {
  272.             int numspc = tabSize - ((i + startx) % tabSize);
  273.             memset(buff + i, ' ', numspc);
  274.             i += numspc;
  275.         }
  276.         else
  277.             buff[i++] = c;
  278.         }
  279.         buff[i] = 0;
  280.         if(c == '\n')
  281.         {
  282.         if(pos->pos_Col)
  283.         {
  284.             if(!insertstrn(tx, buff, i, pos))
  285.             goto abort;
  286.             if(!splitline(tx, pos))
  287.             goto abort;
  288.             pos->pos_Col = 0;
  289.         }
  290.         else
  291.         {
  292.             u_char *copy;
  293.             if(!resizelinelist(tx, +1, pos->pos_Line))
  294.             goto abort;
  295.             if(!(copy = mystrdupn(buff, i)))
  296.             goto abort;
  297.             tx->tx_Lines[pos->pos_Line].ln_Strlen = i + 1;
  298.             tx->tx_Lines[pos->pos_Line].ln_Line = copy;
  299.             keepposaddy(tx, +1, pos->pos_Line);
  300.         }
  301.         pos->pos_Line++;
  302.         startx = 0;
  303.         }
  304.         else
  305.         {
  306.         str--;
  307.         if(!insertstrn(tx, buff, i, pos))
  308.         {
  309. abort:
  310.             settitle(NoMemMsg);
  311.             mystrfree(buff);
  312.             return(FALSE);
  313.         }
  314.         }
  315.     }
  316.     flaginsertion(tx, &orig, pos);
  317.     mystrfree(buff);
  318.     return(TRUE);
  319.     }
  320.     settitle(NoMemMsg);
  321.     return(FALSE);
  322. }
  323.  
  324. /*
  325.  */
  326. bool
  327. rectinsertstring(TX *tx, const u_char *text, POS *pos)
  328. {
  329.     long col = pos->pos_Col;
  330.     LINE *line = tx->tx_Lines + pos->pos_Line;
  331.     long llen = 0;
  332.     POS orig;
  333.     while(*text && (pos->pos_Line < tx->tx_NumLines))
  334.     {
  335.     const u_char *tmp = text;
  336.     while(*tmp && (*tmp != '\n'))
  337.         tmp++;
  338.     llen = tmp - text;
  339.     if(!padpos(tx, pos))
  340.         goto nomem;
  341.     orig = *pos;
  342.     if(!insertstrn(tx, text, llen, pos))
  343.         goto nomem;
  344.     flaginsertion(tx, &orig, pos);
  345.     pos->pos_Line++;
  346.     pos->pos_Col = col;
  347.     line++;
  348.     if(*(text = tmp))
  349.         text++;
  350.     }
  351.     if(*text && pos->pos_Line == tx->tx_NumLines)
  352.     {
  353.     /*
  354.      * Now start expanding off the bottom of the file.
  355.      */
  356.     orig = *pos;
  357.     while(*text)
  358.     {
  359.         const u_char *tmp = text;
  360.         u_char *new;
  361.         while(*tmp && (*tmp != '\n'))
  362.         tmp++;
  363.         llen = tmp - text;
  364.         if(!resizelinelist(tx, +1, pos->pos_Line))
  365.         goto nomem;
  366.         if(!(new = mystralloc(col + llen + 1)))
  367.         goto nomem;
  368.         memset(new, ' ', col);
  369.         memcpy(new + col, text, llen);
  370.         new[col + llen] = 0;
  371.         tx->tx_Lines[pos->pos_Line].ln_Strlen = col + llen + 1;
  372.         tx->tx_Lines[pos->pos_Line].ln_Line = new;
  373.         keepposaddy(tx, +1, pos->pos_Line);
  374.         pos->pos_Line++;
  375.         /*
  376.          * Possible need for keepposaddx() here, my theory is
  377.          * that since i've just created a new line nothing
  378.          * will point to it???
  379.          */
  380.         line++;
  381.         if(*(text = tmp))
  382.         text++;
  383.     }
  384.     if(llen > 0)
  385.         pos->pos_Col += llen;
  386.     pos->pos_Line--;
  387.     flaginsertion(tx, &orig, pos);
  388.     }
  389.     return(TRUE);
  390. nomem:
  391.     settitle(NoMemMsg);
  392.     return(FALSE);
  393. }
  394.  
  395. /*
  396.  * Deletes some text (this line only)
  397.  */
  398. bool
  399. deletechars(TX *tx, const POS *pos, long size)
  400. {
  401.     LINE *line = tx->tx_Lines + pos->pos_Line;
  402.     if(line->ln_Strlen)
  403.     {
  404.     u_char *newline;
  405.     if(size >= line->ln_Strlen)
  406.         size = line->ln_Strlen - 1;
  407.     newline = mystralloc(line->ln_Strlen - size);
  408.     if(newline)
  409.     {
  410.         strncpy(newline, line->ln_Line, pos->pos_Col);
  411.         strcpy(newline + pos->pos_Col, line->ln_Line + pos->pos_Col + size);
  412.         mystrfree(line->ln_Line);
  413.         line->ln_Strlen -= size;
  414.         line->ln_Line = newline;
  415.         keeppossubx(tx, size, pos->pos_Col, pos->pos_Line);
  416.         return(TRUE);
  417.     }
  418.     return(FALSE);
  419.     }
  420.     return(TRUE);
  421. }
  422.  
  423. /*
  424.  * deletes some lines
  425.  */
  426. bool
  427. deletelines(TX *tx, long linenum, long size)
  428. {
  429.     bool rc = FALSE;
  430.     if(resizelinelist(tx, -size, linenum))
  431.     rc = TRUE;
  432.     keeppossuby(tx, size, linenum);
  433.     return(rc);
  434. }
  435.  
  436. /*
  437.  * Deletes from startPos to endPos
  438.  */
  439. bool
  440. deletesection(TX *tx, POS *startPos, POS *endPos)
  441. {
  442.     if(endPos->pos_Line == startPos->pos_Line)
  443.     {
  444.     deletechars(tx, startPos, endPos->pos_Col - startPos->pos_Col);
  445.     flagdeletion(tx, startPos, endPos);
  446.     }
  447.     else
  448.     {
  449.     long middle;
  450.     bool joinflag = FALSE;
  451.     POS orig = *startPos, oend = *endPos;
  452.     if(startPos->pos_Col)
  453.     {
  454.         long start = tx->tx_Lines[startPos->pos_Line].ln_Strlen - startPos->pos_Col - 1;
  455.         if(start)
  456.         deletechars(tx, startPos, start);
  457.         startPos->pos_Col = 0;
  458.         startPos->pos_Line++;
  459.         joinflag = TRUE;
  460.     }
  461.     middle = endPos->pos_Line - startPos->pos_Line;
  462.     if(middle != 0)
  463.     {
  464.         deletelines(tx, startPos->pos_Line, middle);
  465.         endPos->pos_Line -= middle;
  466.     }
  467.     if(endPos->pos_Col)
  468.         deletechars(tx, startPos, endPos->pos_Col);
  469.     if(joinflag && startPos->pos_Line)
  470.     {
  471.         startPos->pos_Line--;
  472.         startPos->pos_Col = tx->tx_Lines[startPos->pos_Line].ln_Strlen - 1;
  473.         joinlines(tx, startPos);
  474.     }
  475.     flagdeletion(tx, &orig, &oend);
  476.     }
  477.     return(TRUE);
  478. }
  479.  
  480. bool
  481. rectdeletesection(TX *tx, POS *startPos, POS *endPos)
  482. {
  483.     LINE *line = tx->tx_Lines + startPos->pos_Line;
  484.     while(startPos->pos_Line <= endPos->pos_Line)
  485.     {
  486.     if(line->ln_Strlen > startPos->pos_Col)
  487.     {
  488.         long dlen;
  489.         POS tmp;
  490.         if(line->ln_Strlen > endPos->pos_Col)
  491.         dlen = (endPos->pos_Col - startPos->pos_Col) + 1;
  492.         else
  493.         dlen = line->ln_Strlen - 1 - startPos->pos_Col;
  494.         if(!deletechars(tx, startPos, dlen))
  495.         {
  496.         settitle(NoMemMsg);
  497.         return(FALSE);
  498.         }
  499.         tmp.pos_Line = startPos->pos_Line;
  500.         tmp.pos_Col = startPos->pos_Col + dlen;
  501.         flagdeletion(tx, startPos, &tmp);
  502.     }
  503.     startPos->pos_Line++;
  504.     line++;
  505.     }
  506.     return(TRUE);
  507. }
  508.  
  509. /*
  510.  * splits the line into two
  511.  */
  512. bool
  513. splitline(TX *tx, POS *pos)
  514. {
  515.     if(resizelinelist(tx, +1, pos->pos_Line + 1))
  516.     {
  517.     LINE *line = tx->tx_Lines + pos->pos_Line;
  518.     u_char *newline1 = mystrdupn(line->ln_Line, pos->pos_Col);
  519.     if(newline1)
  520.     {
  521.         long nl2len = line->ln_Strlen - pos->pos_Col;
  522.         u_char *newline2 = mystrdupn(line->ln_Line + pos->pos_Col,
  523.                      nl2len - 1);
  524.         if(newline2)
  525.         {
  526.         if(line[0].ln_Line)
  527.             mystrfree(line[0].ln_Line);
  528.         line[0].ln_Strlen = pos->pos_Col + 1;
  529.         line[0].ln_Line = newline1;
  530.         if(line[1].ln_Line)
  531.             mystrfree(line[1].ln_Line);
  532.         line[1].ln_Strlen = nl2len;
  533.         line[1].ln_Line = newline2;
  534.         keeppossplity(tx, pos->pos_Col, pos->pos_Line);
  535.         return(TRUE);
  536.         }
  537.         mystrfree(newline1);
  538.     }
  539.     }
  540.     settitle(NoMemMsg);
  541.     return(FALSE);
  542. }
  543.  
  544. /*
  545.  * joins the current line to the line below it.
  546.  */
  547. static bool
  548. joinlines(TX *tx, POS *pos)
  549. {
  550.     if((pos->pos_Line + 1) < tx->tx_NumLines)
  551.     {
  552.     LINE *line1 = tx->tx_Lines + pos->pos_Line;
  553.     LINE *line2 = line1 + 1;
  554.     int newlen = line1->ln_Strlen + line2->ln_Strlen - 1;
  555.     u_char *newstr = mystralloc(newlen);
  556.     if(newstr)
  557.     {
  558.         stpcpy(stpcpy(newstr, line1->ln_Line), line2->ln_Line);
  559.         resizelinelist(tx, -1, pos->pos_Line);
  560.         line1 = tx->tx_Lines + pos->pos_Line;
  561.         if(line1->ln_Line)
  562.         mystrfree(line1->ln_Line);
  563.         line1->ln_Strlen = newlen;
  564.         line1->ln_Line = newstr;
  565.         keepposjoiny(tx, pos->pos_Col, pos->pos_Line);
  566.         return(TRUE);
  567.     }
  568.     settitle(NoMemMsg);
  569.     }
  570.     return(FALSE);
  571. }
  572.  
  573. /*
  574.  * Inserts spaces from end of line to pos
  575.  */
  576. bool
  577. padpos(TX *tx, POS *pos)
  578. {
  579.     if(pos->pos_Line < tx->tx_NumLines)
  580.     {
  581.     LINE *line = tx->tx_Lines + pos->pos_Line;
  582.     if(line->ln_Strlen < (pos->pos_Col + 1))
  583.     {
  584.         u_char *newline = mystralloc(pos->pos_Col + 1);
  585.         if(newline)
  586.         {
  587.         long i;
  588.         strcpy(newline, line->ln_Line);
  589.         for(i = line->ln_Strlen - 1; i < pos->pos_Col; i++)
  590.             newline[i] = ' ';
  591.         newline[i] = 0;
  592.         if(line->ln_Line)
  593.             mystrfree(line->ln_Line);
  594.         line->ln_Strlen = pos->pos_Col + 1;
  595.         line->ln_Line = newline;
  596.         return(TRUE);
  597.         }
  598.         settitle(NoMemMsg);
  599.         pos->pos_Col = line->ln_Strlen - 1;
  600.         return(FALSE);
  601.     }
  602.     return(TRUE);
  603.     }
  604.     return(FALSE);
  605. }
  606.  
  607. bool
  608. padcursor(VW *vw)
  609. {
  610.     return(padpos(vw->vw_Tx, &vw->vw_CursorPos));
  611. }
  612.  
  613. /*
  614.  * if end is before start then swap the two
  615.  */
  616. void
  617. orderpos(POS *start, POS *end)
  618. {
  619.     if(((start->pos_Line == end->pos_Line) && (start->pos_Col > end->pos_Col))
  620.      || (start->pos_Line > end->pos_Line))
  621.     {
  622.     POS temp;
  623.     temp = *start;
  624.     *start = *end;
  625.     *end = temp;
  626.     }
  627. }
  628.  
  629. /*
  630.  * makes sure that the corners of a rectangle are how I want them
  631.  */
  632. void
  633. orderrect(POS *dstart, POS *dend, POS *sstart, POS *send)
  634. {
  635.     if(sstart->pos_Col < send->pos_Col)
  636.     {
  637.     dstart->pos_Col = sstart->pos_Col;
  638.     dend->pos_Col = send->pos_Col;
  639.     }
  640.     else
  641.     {
  642.     dstart->pos_Col = send->pos_Col;
  643.     dend->pos_Col = sstart->pos_Col;
  644.     }
  645.     if(sstart->pos_Line < send->pos_Line)
  646.     {
  647.     dstart->pos_Line = sstart->pos_Line;
  648.     dend->pos_Line = send->pos_Line;
  649.     }
  650.     else
  651.     {
  652.     dstart->pos_Line = send->pos_Line;
  653.     dend->pos_Line = sstart->pos_Line;
  654.     }
  655. }
  656.  
  657. bool
  658. checksect(TX *tx, POS *start, POS *end)
  659. {
  660.     orderpos(start, end);
  661.     if((start->pos_Line >= tx->tx_NumLines) || (end->pos_Line >= tx->tx_NumLines))
  662.     return(FALSE);
  663.     if(start->pos_Col >= tx->tx_Lines[start->pos_Line].ln_Strlen)
  664.     start->pos_Col = tx->tx_Lines[start->pos_Line].ln_Strlen - 1;
  665.     if(end->pos_Col >= tx->tx_Lines[end->pos_Line].ln_Strlen)
  666.     end->pos_Col = tx->tx_Lines[end->pos_Line].ln_Strlen - 1;
  667.     return(TRUE);
  668. }
  669.  
  670. void
  671. checkpos(TX *tx, POS *pos)
  672. {
  673.     if(pos->pos_Line >= tx->tx_NumLines)
  674.     pos->pos_Line = tx->tx_NumLines - 1;
  675.     if(pos->pos_Col >= tx->tx_Lines[pos->pos_Line].ln_Strlen)
  676.     pos->pos_Col = tx->tx_Lines[pos->pos_Line].ln_Strlen - 1;
  677. }
  678.  
  679. bool
  680. checkline(TX *tx, POS *pos)
  681. {
  682.     if((pos->pos_Line >= tx->tx_NumLines) || (pos->pos_Col < 0))
  683.     return(FALSE);
  684.     return(TRUE);
  685. }
  686.  
  687. /*
  688.  * Returns the number of bytes needed to store a section, doesn't include
  689.  * a zero terminator but does include all newline chars.
  690.  */
  691. long
  692. sectionlength(TX *tx, POS *startPos, POS *endPos)
  693. {
  694.     long linenum = startPos->pos_Line;
  695.     LINE *line = tx->tx_Lines + linenum;
  696.     long length;
  697.     if(startPos->pos_Line == endPos->pos_Line)
  698.     length = endPos->pos_Col - startPos->pos_Col;
  699.     else
  700.     {
  701.     length = line->ln_Strlen - startPos->pos_Col;
  702.     linenum++;
  703.     line++;
  704.     while(linenum < endPos->pos_Line)
  705.     {
  706.         length += line->ln_Strlen;
  707.         linenum++;
  708.         line++;
  709.     }
  710.     length += endPos->pos_Col;
  711.     }
  712.     return(length);
  713. }
  714.  
  715. /*
  716.  * Copies a section to a buffer.
  717.  * end of copy does NOT have a zero appended to it.
  718.  */
  719. void
  720. copysection(TX *tx, POS *startPos, POS *endPos, u_char *buff)
  721. {
  722.     long linenum = startPos->pos_Line;
  723.     LINE *line = tx->tx_Lines + linenum;
  724.     long copylen;
  725.     if(startPos->pos_Line == endPos->pos_Line)
  726.     {
  727.     copylen = endPos->pos_Col - startPos->pos_Col;
  728.     memcpy(buff, line->ln_Line + startPos->pos_Col, copylen);
  729.     buff[copylen] = 0;
  730.     }
  731.     else
  732.     {
  733.     copylen = line->ln_Strlen - startPos->pos_Col - 1;
  734.     memcpy(buff, line->ln_Line + startPos->pos_Col, copylen);
  735.     buff[copylen] = '\n';
  736.     buff += copylen + 1;
  737.     linenum++;
  738.     line++;
  739.     while(linenum < endPos->pos_Line)
  740.     {
  741.         copylen = line->ln_Strlen - 1;
  742.         memcpy(buff, line->ln_Line, copylen);
  743.         buff[copylen] = '\n';
  744.         buff += copylen + 1;
  745.         linenum++;
  746.         line++;
  747.     }
  748.     memcpy(buff, line->ln_Line, endPos->pos_Col);
  749.     }
  750. }
  751.  
  752. /*
  753.  * Returns the size of the rectangle (start, end). zero terminator
  754.  * isn't included.
  755.  */
  756. long
  757. rectsectionlength(TX *tx, POS *startPos, POS *endPos)
  758. {
  759.     return(((endPos->pos_Line - startPos->pos_Line) + 1) * ((endPos->pos_Col - startPos->pos_Col) + 2));
  760. }
  761.  
  762. /*
  763.  * Copies a rectangle to a string, newlines are inserted between lines (and
  764.  * after the last line) but \0 terminator is NOT.
  765.  */
  766. void
  767. rectcopysection(TX *tx, POS *startPos, POS *endPos, u_char *buff)
  768. {
  769.     long linenum = startPos->pos_Line;
  770.     LINE *line = tx->tx_Lines + linenum;
  771.     long width = (endPos->pos_Col - startPos->pos_Col) + 1;
  772.     while(linenum <= endPos->pos_Line)
  773.     {
  774.     long copylen = (line->ln_Strlen - 1) - startPos->pos_Col;
  775.     if(copylen <= 0)
  776.         copylen = 0;
  777.     else
  778.     {
  779.         if(copylen > width)
  780.         copylen = width;
  781.         memcpy(buff, line->ln_Line + startPos->pos_Col, copylen);
  782.     }
  783.     if(copylen < width)
  784.         memset(buff + copylen, ' ', width - copylen);
  785.     buff[width] = '\n';
  786.     buff += width + 1;
  787.     linenum++;
  788.     line++;
  789.     }
  790. }
  791.  
  792. /*
  793.  * Copies the end of each line from column codePos[0] between the cP[1] and
  794.  * cP[2] lines (exclusively).
  795.  */
  796. bool
  797. colcopysect(TX *tx, long *codePos, u_char *buf, long bufLen)
  798. {
  799.     LINE *line;
  800.     codePos[1]++;
  801.     line = tx->tx_Lines + codePos[1];
  802.     while(codePos[1] < codePos[2])
  803.     {
  804.     int clen = line->ln_Strlen - codePos[0];
  805.     if((clen > 0) && (clen < bufLen))
  806.     {
  807.         memcpy(buf, line->ln_Line + codePos[0], clen - 1);
  808.         buf[clen - 1] = '\n';
  809.         buf += clen;
  810.         bufLen -= clen;
  811.     }
  812.     line++;
  813.     codePos[1]++;
  814.     }
  815.     *buf = 0;
  816.     return(TRUE);
  817. }
  818.  
  819. /*
  820.  * returns TRUE if the specified position is inside a block
  821.  */
  822. bool
  823. posinblock(VW *vw, long col, long line)
  824. {
  825.     if((vw->vw_BlockStatus)
  826.        || (line < vw->vw_BlockS.pos_Line)
  827.        || (line > vw->vw_BlockE.pos_Line))
  828.     return(FALSE);
  829.     if(vw->vw_Flags & VWFF_RECTBLOCKS)
  830.     {
  831.     if(vw->vw_BlockS.pos_Col < vw->vw_BlockE.pos_Col)
  832.     {
  833.         if((col < vw->vw_BlockS.pos_Col) || (col > vw->vw_BlockE.pos_Col))
  834.         return(FALSE);
  835.     }
  836.     else
  837.     {
  838.         if((col < vw->vw_BlockE.pos_Col) || (col > vw->vw_BlockS.pos_Col))
  839.         return(FALSE);
  840.     }
  841.     }
  842.     else
  843.     {
  844.     if(((line == vw->vw_BlockS.pos_Line) && (col < vw->vw_BlockS.pos_Col))
  845.       || ((line == vw->vw_BlockE.pos_Line) && (col >= vw->vw_BlockE.pos_Col)))
  846.     return(FALSE);
  847.     }
  848.     return(TRUE);
  849. }
  850.  
  851. bool
  852. cursinblock(VW *vw)
  853. {
  854.     return(posinblock(vw, vw->vw_CursorPos.pos_Col, vw->vw_CursorPos.pos_Line));
  855. }
  856.  
  857.  
  858. bool
  859. pageinblock(VW *vw)
  860. {
  861.     if((vw->vw_BlockStatus)
  862.       || ((vw->vw_BlockE.pos_Line + 1) < vw->vw_StartLine)
  863.       || (vw->vw_BlockS.pos_Line > vw->vw_StartLine + vw->vw_MaxY))
  864.     return(FALSE);
  865.     return(TRUE);
  866. }
  867.  
  868. /*
  869.  * these returns,
  870.  *
  871.  *    0   line not in block
  872.  *    1   whole line in block
  873.  *    2   start of line in block
  874.  *    3   end of line in block
  875.  *    4   middle of line in block
  876.  *
  877.  * note:
  878.  *  this isn't very intelligent (but it works :-).
  879.  *
  880.  * now handles rectangular blocks (VWFF_RECTBLOCKS)
  881.  */
  882. short
  883. lineinblock(VW *vw, long line)
  884. {
  885.     bool startin = FALSE;
  886.     bool endin = FALSE;
  887.  
  888.     if((vw->vw_BlockStatus)
  889.       || (line < vw->vw_BlockS.pos_Line)
  890.       || (line > vw->vw_BlockE.pos_Line))
  891.     return(0);
  892.     if(vw->vw_Flags & VWFF_RECTBLOCKS)
  893.     {
  894.     /*
  895.      * ???
  896.      */
  897. /*
  898.  *    if(vw->vw_BlockS.pos_Col != 0)
  899.  *        startin = TRUE;
  900.  *    if(vw->vw_BlockE.pos_Col <= vw->vw_Tx->tx_Lines[line].ln_Strlen)
  901.  *        endin = TRUE;
  902.  */
  903.     startin = endin = 4;
  904.     }
  905.     else
  906.     {
  907.     if(line == vw->vw_BlockE.pos_Line)
  908.         startin = TRUE;
  909.     if(line == vw->vw_BlockS.pos_Line)
  910.         endin = TRUE;
  911.     }
  912.     if(startin)
  913.     {
  914.     if(endin)
  915.         return(4);
  916.     return(2);
  917.     }
  918.     if(endin)
  919.     return(3);
  920.     return(1);
  921. }
  922.  
  923. /*
  924.  * makes sure that the marked block is valid
  925.  */
  926. void
  927. orderblock(VW *vw)
  928. {
  929.     if(!vw->vw_BlockStatus)
  930.     {
  931.     if(vw->vw_BlockS.pos_Line > vw->vw_BlockE.pos_Line)
  932.     {
  933.         POS temp = vw->vw_BlockE;
  934.         vw->vw_BlockE = vw->vw_BlockS;
  935.         vw->vw_BlockS = temp;
  936.     }
  937.     else if(vw->vw_BlockS.pos_Line == vw->vw_BlockE.pos_Line)
  938.     {
  939.         if(vw->vw_BlockS.pos_Col > vw->vw_BlockE.pos_Col)
  940.         {
  941.         POS temp = vw->vw_BlockE;
  942.         vw->vw_BlockE = vw->vw_BlockS;
  943.         vw->vw_BlockS = temp;
  944.         }
  945.     }
  946.     }
  947. }
  948.  
  949. /*
  950.  * Set up the refresh flags to refresh the block in the most efficient manner.
  951.  */
  952. void
  953. setblockrefresh(VW *vw)
  954. {
  955.     long endline = vw->vw_StartLine + vw->vw_MaxY;
  956.     if((vw->vw_BlockS.pos_Line > endline)
  957.        || (vw->vw_BlockE.pos_Line < vw->vw_StartLine))
  958.     return;
  959.     if(vw->vw_Flags & VWFF_REFRESH_BLOCK)
  960.     /*
  961.      * If the all-powerful bit is already set I have no way of telling
  962.      * myself to refresh two blocked areas, stupid huh? Anyway I just
  963.      * blast in the whole window instead.
  964.      */
  965.     vw->vw_Flags |= VWFF_FORCE_REFRESH;
  966.     else
  967.     vw->vw_Flags |= VWFF_REFRESH_BLOCK;
  968. }
  969.  
  970. bool
  971. readonly(TX *tx)
  972. {
  973.     if(tx->tx_Flags & TXFF_RDONLY)
  974.     {
  975.     cmd_signal(sym_buffer_read_only, LIST_1(tx));
  976.     return(TRUE);
  977.     }
  978.     else
  979.     return(FALSE);
  980. }
  981.