home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Professional / OS2PRO194.ISO / os2 / editor / beav / line.c < prev    next >
C/C++ Source or Header  |  1994-01-30  |  15KB  |  602 lines

  1. /*
  2. *       Text line handling.
  3. * The functions in this file
  4. * are a general set of line management
  5. * utilities. They are the only routines that
  6. * touch the text. They also touch the buffer
  7. * and window structures, to make sure that the
  8. * necessary updating gets done. There are routines
  9. * in this file that handle the kill buffer too.
  10. * It isn't here for any good reason.
  11. *
  12. * Note that this code only updates the dot and
  13. * mark values in the window list. Since all the code
  14. * acts on the current window, the buffer that we
  15. * are editing must be being displayed, which means
  16. * that "b_nwnd" is non zero, which means that the
  17. * dot and mark values in the buffer headers are
  18. * nonsense.
  19. */
  20.  
  21. #include    "def.h"
  22.  
  23. void l_fix_up ();
  24.  
  25. extern char MSG_cnt_alloc[];
  26. #if RUNCHK
  27. extern char ERR_no_alloc[];
  28. extern char ERR_db_dalloc[];
  29. extern char ERR_lock[];
  30. extern char ERR_lock_del[];
  31. #endif
  32.  
  33. extern LINE *cur_pat;
  34. extern LINE *cur_mask;
  35. extern bool read_pat_mode;
  36. extern BUFFER sav_buf;
  37.  
  38. /*
  39. * This routine allocates a block
  40. * of memory large enough to hold a LINE
  41. * containing "size" characters. Return a pointer
  42. * to the new block, or NULL if there isn't
  43. * any memory left. Print a message in the
  44. * message line if no space.
  45. */
  46. LINE *
  47. lalloc (size)
  48.     register int size;
  49. {
  50.     register LINE *lp;
  51.     char buf[NCOL], buf1[NCOL];
  52. #if NOT_RUNCHK
  53.     if (read_pat_mode)
  54.     printf (ERR_no_alloc);
  55. #endif
  56.  
  57.     if ((lp = (LINE *) malloc (sizeof (LINE) + size)) == NULL)
  58.     {
  59.     sprintf (buf1, MSG_cnt_alloc, R_POS_FMT (curwp));
  60.     sprintf (buf, buf1, (A32) size);
  61.     err_echo (buf);
  62.     curbp->b_flag |= BFBAD;    /* may be trashed */
  63.     curwp->w_flag |= WFMODE;
  64.     update ();
  65.     return (NULL);
  66.     }
  67.     lp->l_size = size;
  68.     lp->l_used = 0;
  69.     lp->l_file_offset = 0;    /* set resonable initial value */
  70.     return (lp);
  71. }
  72.  
  73. /*
  74. * Delete line "lp". Fix all of the
  75. * links that might point at it (they are
  76. * moved to offset 0 of the next line.
  77. * Unlink the line from whatever buffer it
  78. * might be in. Release the memory. The
  79. * buffers are updated too; the magic conditions
  80. * described in the above comments don't hold
  81. * here.
  82. */
  83.  
  84. void
  85. lfree (lp)
  86.     register LINE *lp;
  87. {
  88.     register BUFFER *bp;
  89.     register WINDOW *wp;
  90.  
  91. #if NOT_RUNCHK
  92.     if (read_pat_mode)
  93.     printf (ERR_db_dalloc);
  94. #endif
  95.  
  96.     wp = wheadp;
  97.     while (wp != NULL)
  98.     {
  99.     if (wp->w_linep == lp)
  100.     {
  101.         wp->w_linep = lp->l_fp;
  102.         wp->w_loff = 0;
  103.     }
  104.  
  105.     if (wp->w_dotp == lp)
  106.     {
  107.         wp->w_dotp = lp->l_fp;
  108.         wp->w_doto = 0;
  109.     }
  110.  
  111.     if (wp->w_markp == lp)
  112.     {
  113.         wp->w_markp = lp->l_fp;
  114.         wp->w_marko = 0;
  115.     }
  116.  
  117.     wp = wp->w_wndp;
  118.     }
  119.  
  120.     bp = bheadp;
  121.     while (bp != NULL)
  122.     {
  123.     if (bp->b_nwnd == 0)
  124.     {
  125.         if (bp->b_dotp == lp)
  126.         {
  127.         bp->b_dotp = lp->l_fp;
  128.         bp->b_doto = 0;
  129.         }
  130.  
  131.         if (bp->b_markp == lp)
  132.         {
  133.         bp->b_markp = lp->l_fp;
  134.         bp->b_marko = 0;
  135.         }
  136.     }
  137.     bp = bp->b_bufp;
  138.     }
  139.  
  140.     lp->l_bp->l_fp = lp->l_fp;
  141.     lp->l_fp->l_bp = lp->l_bp;
  142.     free ((char *) lp);
  143. }
  144.  
  145. /*
  146. * This routine gets called when
  147. * a character is changed in place in the
  148. * current buffer. It updates all of the required
  149. * flags in the buffer and window system. The flag
  150. * used is passed as an argument; if the buffer is being
  151. * displayed in more than 1 window we change EDIT to
  152. * HARD. Set MODE if the mode line needs to be
  153. * updated (the "*" has to be set).
  154. */
  155. void
  156. lchange (flag)
  157.     register int flag;
  158. {
  159.     register WINDOW *wp;
  160.  
  161.     if (curbp->b_nwnd != 1)    /* Ensure hard.     */
  162.     flag = WFHARD;
  163.     if ((curbp->b_flag & BFCHG) == 0)
  164.     {
  165.     /* First change, so     */
  166.     flag |= WFMODE;        /* update mode lines.   */
  167.     curbp->b_flag |= BFCHG;
  168.     }
  169.  
  170.     wp = wheadp;
  171.     while (wp != NULL)
  172.     {
  173.     if (wp->w_bufp == curbp)
  174.         wp->w_flag |= flag;
  175.     wp = wp->w_wndp;
  176.     }
  177. }
  178.  
  179. /*
  180.  *  Break the line "dotp" in two at the position "doto."
  181.  */
  182.  
  183. LINE *
  184. l_break_in_two (lp, lo, extra)
  185.     register LINE *lp;
  186.     register LPOS lo, extra;
  187. {
  188.     register LINE *new_lp;
  189.     register D8 *cp1;
  190.     register D8 *cp2;
  191.     LPOS cnt, i;
  192.  
  193.     i = 0;
  194.     cnt = lp->l_used - lo;
  195.     if ((new_lp = lalloc (cnt + extra)) == NULL)
  196.     return (NULL);
  197.  
  198.     cp1 = &lp->l_text[lo];    /* starting location, source */
  199.     cp2 = &new_lp->l_text[0];    /* starting location, destination */
  200.  
  201.     /* kill bytes in the current line */
  202.     while (i++ < cnt)
  203.     {
  204.     *cp2++ = *cp1++;
  205.     }
  206.     lp->l_used -= cnt;
  207.     new_lp->l_used = cnt;
  208.     new_lp->l_file_offset = new_lp->l_file_offset + lo;
  209.  
  210.     /* insert into chain */
  211.     new_lp->l_fp = lp->l_fp;
  212.     lp->l_fp = new_lp;
  213.     new_lp->l_bp = lp;
  214.     new_lp->l_fp->l_bp = new_lp;
  215.     return (new_lp);
  216. }
  217.  
  218. /*
  219. * Insert "n" copies of the character "c"
  220. * at the current location of dot. In the easy case
  221. * all that happens is the text is stored in the line.
  222. * Always allocate some extra space in line so that edit
  223. * will be faster next time but will save space in the general case.
  224. * In the hard case, the line has to be reallocated.
  225. * When the window list is updated, take special
  226. * care; I screwed it up once. You always update dot
  227. * in the current window. You update mark, and a
  228. * dot in another window, if it is greater than
  229. * the place where you did the insert. Return TRUE
  230. * if all is well, and FALSE on errors.
  231. */
  232. bool
  233. linsert (n, c)
  234.     int n, c;
  235. {
  236.     register D8 *cp1;
  237.     register D8 *cp2;
  238.     register LINE *lp1;
  239.     register LINE *lp2;
  240.     register short doto;
  241.     register int i;
  242.     register WINDOW *wp;
  243.  
  244. #if RUNCHK
  245.     /* check that buffer size can be changed */
  246.     if (curbp->b_flag & BFSLOCK)
  247.     {
  248.     writ_echo (ERR_lock);
  249.     return (FALSE);
  250.     }
  251. #endif
  252.  
  253.     lchange (WFMOVE);
  254.     lp1 = curwp->w_dotp;    /* Current line     */
  255.     if (lp1 == curbp->b_linep)
  256.     {
  257.     /* At the end: special  */
  258.     /* break the current line at the end */
  259.     if ((lp2 = l_break_in_two (lp1, lp1->l_used, (LPOS) n + NBLOCK)) == NULL)
  260.         return (FALSE);
  261.     for (i = 0; i < n; ++i)    /* Add the characters   */
  262.         lp2->l_text[i] = (uchar) c;
  263.     lp2->l_used = n;
  264.     curwp->w_dotp = lp2;
  265.     curwp->w_doto = n;
  266.     return (TRUE);
  267.     }
  268.  
  269.     doto = curwp->w_doto;    /* Save for later.  */
  270.     if (lp1->l_used + n > lp1->l_size)
  271.     {
  272.     /* break the current line and let the normal insert do it */
  273.     if ((lp2 = l_break_in_two (lp1, doto, (LPOS) n + NBLOCK)) == NULL)
  274.         return (FALSE);
  275.     lp1->l_text[doto] = (uchar) c;
  276.     lp1->l_used++;
  277.     curwp->w_doto++;
  278.     if (curwp->w_doto >= lp1->l_used)
  279.     {
  280.         curwp->w_dotp = lp2;
  281.         curwp->w_doto = 0;
  282.     }
  283.     if (n > 1)
  284.         return (linsert (n - 1, c));    /* handle the rest in normal maner */
  285.     }
  286.     else
  287.     {
  288.     /* Easy: in place   */
  289.     lp2 = lp1;        /* Pretend new line */
  290.     lp2->l_used += n;
  291.     cp2 = &lp1->l_text[lp1->l_used];
  292.     cp1 = cp2 - n;
  293.     while (cp1 != &lp1->l_text[doto])
  294.         *--cp2 = *--cp1;
  295.     for (i = 0; i < n; ++i)    /* Add the characters   */
  296.         lp2->l_text[doto + i] = (uchar) c;
  297.     move_ptr (curwp, (A32) n, TRUE, TRUE, TRUE);
  298.     }
  299.  
  300.     wp = wheadp;        /* Update windows   */
  301.     while (wp != NULL)
  302.     {
  303.     if ((wp->w_linep == lp1) && (wp->w_loff >= lp1->l_used))
  304.     {
  305.         wp->w_linep = lp2;
  306.         wp->w_loff -= lp1->l_used;
  307.     }
  308.  
  309.     /* move dot to next line but not to head line */
  310.     if ((wp->w_dotp == lp1) && (wp->w_doto >= lp1->l_used) &&
  311.         (wp->w_dotp->l_fp->l_size != 0))
  312.     {
  313.         wp->w_dotp = lp2;
  314.         wp->w_doto -= (lp1->l_used - 1);
  315.     }
  316.  
  317.     if ((wp->w_markp == lp1) && (wp->w_marko >= lp1->l_used))
  318.     {
  319.         wp->w_markp = lp2;
  320.         wp->w_marko -= (lp1->l_used - 1);
  321.     }
  322.  
  323.     wp = wp->w_wndp;
  324.     }
  325.     l_fix_up (lp1);        /* re-adjust file offsets */
  326.     return (TRUE);
  327. }
  328.  
  329. /*
  330. * This function deletes n_bytes,
  331. * starting at dot. It understands how to deal
  332. * with end of lines, etc. It returns TRUE if all
  333. * of the characters were deleted, and FALSE if
  334. * they were not (because dot ran into the end of
  335. * the buffer). The "kflag" is TRUE if the text
  336. * should be put in the kill buffer.
  337. */
  338. bool
  339. ldelete (n_bytes, kflag)
  340.     A32 n_bytes;
  341.     int kflag;
  342. {
  343.     register LINE *dotp, *lp, *lp_prev, *lp_next;
  344.     register LPOS doto, l_cnt;
  345.     register WINDOW *wp;
  346.     D8 *cp1, *cp2;
  347.     D32 dot_pos;
  348.     uint n_byt;
  349.  
  350. #if RUNCHK
  351.     /* check that buffer size can be changed */
  352.     if (curbp->b_flag & BFSLOCK)
  353.     {
  354.     writ_echo (ERR_lock_del);
  355.     return (FALSE);
  356.     }
  357. #endif
  358.     lchange (WFMOVE);
  359.     doto = curwp->w_doto;
  360.     dotp = curwp->w_dotp;
  361.     lp_prev = dotp->l_bp;
  362.     dot_pos = DOT_POS (curwp);
  363.  
  364.     /* if at the end of the buffer then delete nothing */
  365.     if (dot_pos >= BUF_SIZE (curwp))
  366.     {
  367.     l_fix_up (dotp);    /* re-adjust file offsets */
  368.     return (TRUE);
  369.     }
  370.  
  371.     /* save dot and mark positions for later restore */
  372.     wp = wheadp;
  373.     while (wp != NULL)
  374.     {
  375.     wp->w_dot_temp = DOT_POS (wp);
  376.     if (wp->w_markp != NULL)/* mark may not be set */
  377.         wp->w_mark_temp = MARK_POS (wp);
  378.     wp->w_wind_temp = WIND_POS (wp);
  379.     wp = wp->w_wndp;
  380.     }
  381.  
  382.     /* is delete wholy within one line? */
  383.     if ((doto + n_bytes) < dotp->l_used)
  384.     {
  385.     cp1 = &dotp->l_text[doto];    /* Scrunch text.    */
  386.     cp2 = cp1 + n_bytes;
  387.  
  388.     /* put stuff to delete into the kill buffer */
  389.     if (kflag != FALSE)
  390.     {
  391.         /* Kill?        */
  392.         while (cp1 != cp2)
  393.         {
  394.         if (b_append_c (&sav_buf, *cp1) == FALSE)
  395.             return (FALSE);
  396.         ++cp1;
  397.         }
  398.         cp1 = &dotp->l_text[doto];
  399.     }
  400.     /* kill bytes in the current line */
  401.     while (cp2 < &dotp->l_text[dotp->l_used])
  402.         *cp1++ = *cp2++;
  403.  
  404.     dotp->l_used -= n_bytes;
  405.     }
  406.     else
  407.     {                /* wholesale delete by moving lines to save buffer */
  408.     if (doto != 0)
  409.     {
  410.         if ((lp = l_break_in_two (dotp, doto, 0)) == NULL)
  411.         return (FALSE);
  412.     }
  413.     else
  414.         lp = dotp;
  415.  
  416.     n_byt = n_bytes;
  417.     /* now handle whole lines if necessary */
  418.     while (n_byt > 0)
  419.     {
  420.         lp_next = lp->l_fp;
  421.  
  422.         if (n_byt < lp->l_used)
  423.         {
  424.         /* get last piece of a line */
  425.         lp_next = l_break_in_two (lp, n_byt, 0);
  426.         }
  427.         n_byt -= lp->l_used;
  428.         if (kflag)
  429.         {
  430.         /* remove form linked list */
  431.         lp->l_bp->l_fp = lp->l_fp;
  432.         lp->l_fp->l_bp = lp->l_bp;
  433.         /* append it to the save buffer */
  434.         b_append_l (&sav_buf, lp);
  435.         }
  436.         else
  437.         /* if we don't want it, free it */
  438.         lfree (lp);
  439.         lp = lp_next;
  440.     }
  441.     }
  442.     l_fix_up (lp_prev);        /* re-adjust file offsets */
  443.  
  444.     /* adjust dot and marks in other windows */
  445.     /* this should be ok because the save buffer dosn't disturb l_file_offset */
  446.     wp = wheadp;        /* Fix windows      */
  447.     while (wp != NULL)
  448.     {
  449.     if (curbp == wp->w_bufp)
  450.     {
  451.         A32 temp;
  452.  
  453.         /* if dot is before delete position, do nothing */
  454.         if (dot_pos <= (temp = wp->w_dot_temp))
  455.         {
  456.         /* step back to the previous line */
  457.         wp->w_doto = 0;
  458.         wp->w_dotp = lp_prev;
  459.  
  460.         /* if dot is in deleted range, set to dot position */
  461.         if (temp > dot_pos + n_bytes)
  462.             /* if after deleted range, move back deleted ammount */
  463.             move_ptr (wp, temp - n_bytes, TRUE, TRUE, FALSE);
  464.         else
  465.             /* if in the deleted range, move to curwp dot position */
  466.             move_ptr (wp, dot_pos, TRUE, TRUE, FALSE);
  467.         }
  468.         /* mark may not be set in some windows */
  469.         if (wp->w_markp != NULL)
  470.         {
  471.         /* do the same for mark */
  472.         if (dot_pos <= (temp = wp->w_mark_temp))
  473.         {
  474.             /* if in or after the deleted range, move to curwp dot position */
  475.             wp->w_marko = curwp->w_doto;
  476.             wp->w_markp = curwp->w_dotp;
  477.  
  478.             /* if mark after deleted range */
  479.             if (temp > dot_pos + n_bytes)
  480.             {
  481.             /* if after deleted range, move back deleted ammount */
  482.             /* move dot then swap with mark to produce result */
  483.             move_ptr (wp, temp - n_bytes, TRUE, TRUE, FALSE);
  484.             lp_next = wp->w_dotp;
  485.             wp->w_dotp = wp->w_markp;
  486.             wp->w_markp = lp_next;
  487.             l_cnt = wp->w_doto;
  488.             wp->w_doto = wp->w_marko;
  489.             wp->w_marko = l_cnt;
  490.             }
  491.         }
  492.         }
  493.         /* if window position is before delete position, do nothing */
  494.         if (dot_pos <= (temp = wp->w_wind_temp))
  495.         {
  496.         /* set window position to dot position */
  497.         wp->w_loff = 0;
  498.         wp->w_linep = wp->w_dotp;
  499.         wind_on_dot (wp);
  500.         }
  501.     }
  502.     wp = wp->w_wndp;
  503.     }
  504.     /* update buffer display */
  505.     if ((blistp->b_nwnd != 0) &&
  506.     (blistp->b_type == BTLIST))
  507.     listbuffers ();
  508.     return (TRUE);
  509. }
  510.  
  511. /*
  512. *   Replace character at dot position.
  513. */
  514. void
  515. lreplace (n, c)
  516.     int n;
  517.     char c;
  518. {
  519.     lchange (WFEDIT);
  520.     while (n--)
  521.     {
  522.     DOT_CHAR (curwp) = c & 0xff;
  523.     move_ptr (curwp, 1L, TRUE, FALSE, TRUE);
  524.     }
  525. }
  526.  
  527. /*
  528. * Replace plen characters before dot with argument string.
  529. */
  530. bool
  531. lrepl_str (plen, rstr, mstr)
  532.  
  533.     register int plen;        /* length to remove     */
  534.     register LINE *rstr;    /* replace string       */
  535.     register LINE *mstr;    /* mask string       */
  536. {
  537.     register int i;        /* used for random characters   */
  538.     register A32 dot_pos;    /* dot offset into buffer     */
  539.     register int rlen;        /* rplace string length */
  540.     register char c;        /* temp storage for char */
  541.     register char mask;        /* temp storage for mask */
  542.  
  543.     /*
  544.   * make the string lengths match (either pad the line
  545.   * so that it will fit, or scrunch out the excess).
  546.   * be careful with dot's offset.
  547.   */
  548.     /* get offset from begining of buffer */
  549.     dot_pos = DOT_POS (curwp);
  550.     rlen = rstr->l_used;
  551.     if (plen > rlen)
  552.     {
  553.     ldelete ((A32) (plen - rlen), FALSE);
  554.     }
  555.     else if (plen < rlen)
  556.     {
  557.     if (linsert (rlen - plen, ' ') == FALSE)
  558.         return (FALSE);
  559.     }
  560.     /* must use move_ptr because delete may advance to next line */
  561.     move_ptr (curwp, dot_pos, TRUE, FALSE, FALSE);
  562.  
  563.     /* do the replacement. */
  564.     for (i = 0; i < rlen; i++)
  565.     {
  566.     c = DOT_CHAR (curwp);
  567.     mask = mstr->l_text[i];
  568.     DOT_CHAR (curwp) = (c & mask) | (rstr->l_text[i] & ~mask);
  569.     move_ptr (curwp, 1L, TRUE, FALSE, TRUE);
  570.     }
  571.     move_ptr (curwp, dot_pos, TRUE, FALSE, FALSE);
  572.     lchange (WFHARD);
  573.     return (TRUE);
  574. }
  575.  
  576. /*
  577. *   Line fixup.
  578. *   This fixes the 'l_file_offset' variable in
  579. *   each line structure.
  580. *   This is necessary after every change in the size
  581. *   of the buffer.
  582. */
  583. void
  584. l_fix_up (line)
  585.  
  586.     LINE *line;            /* points to buffer header line */
  587.  
  588. {
  589.     long offset;
  590.  
  591.     offset = line->l_file_offset;    /* starting offset */
  592.     offset += line->l_used;
  593.     for (;;)
  594.     {
  595.     line = line->l_fp;
  596.     if (line->l_size == 0)
  597.         return;
  598.     line->l_file_offset = offset;
  599.     offset += line->l_used;
  600.     }
  601. }
  602.