home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 2 / 2875 / line.c next >
Encoding:
C/C++ Source or Header  |  1991-02-28  |  17.8 KB  |  693 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. bool ldelnewline_p ();
  24. void l_fix_up ();
  25. bool kinsert ();
  26.  
  27.  
  28. extern    char    MSG_cnt_alloc[];
  29. extern    char    MSG_too_m_k[];
  30. #if RUNCHK
  31. extern    char    ERR_no_alloc[];
  32. extern    char    ERR_db_dalloc[];
  33. extern    char    ERR_lock[];
  34. extern    char    ERR_lock_del[];
  35. #endif
  36. #include    "lintfunc.dec"
  37.  
  38. #ifndef KBLOCK
  39. #define KBLOCK  256             /* Kill buffer block size.  */
  40. #endif
  41.  
  42. char   *kbufp = NULL;           /* Kill buffer data.        */
  43. int     kused = 0;              /* # of bytes used in KB.   */
  44. int     ksize = 0;              /* # of bytes allocated in KB.  */
  45.  
  46. extern  LINE    *cur_pat;
  47. extern  LINE    *cur_mask;
  48. extern  bool    read_pat_mode;
  49.  
  50. /*
  51. * This routine allocates a block
  52. * of memory large enough to hold a LINE
  53. * containing "used" characters. The block is
  54. * always rounded up a bit. Return a pointer
  55. * to the new block, or NULL if there isn't
  56. * any memory left. Print a message in the
  57. * message line if no space.
  58. */
  59. LINE * lalloc (used)
  60. register int    used;
  61. {
  62.     register    LINE * lp;
  63.     char    buf[80], buf1[50];
  64. #if RUNCHK
  65.     if (read_pat_mode)
  66.         printf (ERR_no_alloc);
  67. #endif
  68.  
  69.     if ((lp = (LINE *) malloc (sizeof (LINE) + used)) == NULL)
  70.         {
  71.         sprintf (buf1, MSG_cnt_alloc, R_POS_FMT(curwp));
  72.         sprintf (buf, buf1, (A32)used);
  73.         writ_echo (buf);
  74.         curbp -> b_flag |= BFBAD;/* may be trashed */
  75.         curwp -> w_flag |= WFMODE;
  76.         update ();
  77.         return (NULL);
  78.         }
  79.     lp -> l_size = used;
  80.     lp -> l_used = used;
  81.     lp -> l_file_offset = 0;    /* set resonable initial value */
  82.     return (lp);
  83. }
  84.  
  85.  
  86. /*
  87. * Delete line "lp". Fix all of the
  88. * links that might point at it (they are
  89. * moved to offset 0 of the next line.
  90. * Unlink the line from whatever buffer it
  91. * might be in. Release the memory. The
  92. * buffers are updated too; the magic conditions
  93. * described in the above comments don't hold
  94. * here.
  95. */
  96.  
  97. void lfree (lp)
  98. register    LINE * lp;
  99. {
  100.     register    BUFFER * bp;
  101.     register    WINDOW * wp;
  102.  
  103. #if RUNCHK
  104.     if (read_pat_mode)
  105.         printf (ERR_db_dalloc);
  106. #endif
  107.  
  108.     wp = wheadp;
  109.     while (wp != NULL)
  110.         {
  111.         if (wp -> w_linep == lp)
  112.             {
  113.             wp -> w_linep = lp -> l_fp;
  114.             wp -> w_loff = 0;
  115.             }
  116.  
  117.         if (wp -> w_dotp == lp)
  118.             {
  119.             wp -> w_dotp = lp -> l_fp;
  120.             wp -> w_doto = 0;
  121.             }
  122.  
  123.         if (wp -> w_markp == lp)
  124.             {
  125.             wp -> w_markp = lp -> l_fp;
  126.             wp -> w_marko = 0;
  127.             }
  128.  
  129.         wp = wp -> w_wndp;
  130.         }
  131.  
  132.     bp = bheadp;
  133.     while (bp != NULL)
  134.         {
  135.  
  136.         if (bp -> b_nwnd == 0)
  137.             {
  138.             if (bp -> b_dotp == lp)
  139.                 {
  140.                 bp -> b_dotp = lp -> l_fp;
  141.                 bp -> b_doto = 0;
  142.                 }
  143.  
  144.             if (bp -> b_markp == lp)
  145.                 {
  146.                 bp -> b_markp = lp -> l_fp;
  147.                 bp -> b_marko = 0;
  148.                 }
  149.             }
  150.         bp = bp -> b_bufp;
  151.         }
  152.  
  153.     lp -> l_bp -> l_fp = lp -> l_fp;
  154.     lp -> l_fp -> l_bp = lp -> l_bp;
  155.     free ((char *) lp);
  156. }
  157.  
  158.  
  159. /*
  160. * This routine gets called when
  161. * a character is changed in place in the
  162. * current buffer. It updates all of the required
  163. * flags in the buffer and window system. The flag
  164. * used is passed as an argument; if the buffer is being
  165. * displayed in more than 1 window we change EDIT to
  166. * HARD. Set MODE if the mode line needs to be
  167. * updated (the "*" has to be set).
  168. */
  169. void lchange (flag)
  170. register int    flag;
  171. {
  172.     register    WINDOW * wp;
  173.  
  174.     if (curbp -> b_nwnd != 1)   /* Ensure hard.     */
  175.         flag = WFHARD;
  176.     if ((curbp -> b_flag & BFCHG) == 0)
  177.         {
  178.     /* First change, so     */
  179.         flag |= WFMODE;         /* update mode lines.   */
  180.         curbp -> b_flag |= BFCHG;
  181.         }
  182.  
  183.     wp = wheadp;
  184.     while (wp != NULL)
  185.         {
  186.         if (wp -> w_bufp == curbp)
  187.             wp -> w_flag |= flag;
  188.         wp = wp -> w_wndp;
  189.         }
  190. }
  191.  
  192.  
  193. /*
  194. * Insert "n" copies of the character "c"
  195. * at the current location of dot. In the easy case
  196. * all that happens is the text is stored in the line.
  197. * Always allocate some extra space in line so that edit 
  198. * will be faster next time but will save space in the general case.
  199. * In the hard case, the line has to be reallocated.
  200. * When the window list is updated, take special
  201. * care; I screwed it up once. You always update dot
  202. * in the current window. You update mark, and a
  203. * dot in another window, if it is greater than
  204. * the place where you did the insert. Return TRUE
  205. * if all is well, and FALSE on errors.
  206. */
  207. bool linsert (n, c)
  208. {
  209.     register char  *cp1;
  210.     register char  *cp2;
  211.     register    LINE * lp1;
  212.     register    LINE * lp2;
  213.     register    LINE * lp3;
  214.     register short  doto;
  215.     register int    i;
  216.     register    WINDOW * wp;
  217.  
  218. #if RUNCHK
  219.     /* check that buffer size can be changed */
  220.     if (curbp -> b_flag & BFSLOCK)
  221.     {
  222.         writ_echo (ERR_lock);
  223.         return (FALSE);
  224.     }
  225. #endif
  226.  
  227.     lchange (WFMOVE);
  228.     lp1 = curwp -> w_dotp;      /* Current line     */
  229.     if (lp1 == curbp -> b_linep)
  230.         {
  231.        /* At the end: special  */
  232.         if ((lp2 = lalloc (n + NBLOCK)) == NULL)/* Allocate new line    */
  233.             return (FALSE);
  234.         lp2 -> l_used = n;      /* set to correct size */
  235.         lp3 = lp1 -> l_bp;      /* Previous line    */
  236.         lp3 -> l_fp = lp2;      /* Link in      */
  237.         lp2 -> l_fp = lp1;
  238.         lp1 -> l_bp = lp2;
  239.         lp2 -> l_bp = lp3;
  240.         for (i = 0; i < n; ++i)
  241.             lp2 -> l_text[i] = c;
  242.         curwp -> w_dotp = lp2;
  243.         curwp -> w_doto = n;
  244.         lp2 -> l_file_offset = lp1 -> l_file_offset + lp1 -> l_used;
  245.         l_fix_up (lp2); /* re-adjust file offsets */
  246.         return (TRUE);
  247.         }
  248.  
  249.     doto = curwp -> w_doto;     /* Save for later.  */
  250.     if (lp1 -> l_used + n > lp1 -> l_size)
  251.         {
  252.     /* Hard: reallocate */
  253.         if ((lp2 = lalloc (lp1 -> l_used + n + NBLOCK)) == NULL)
  254.             return (FALSE);
  255.         lp2 -> l_used = lp1 -> l_used + n;  /* set to correct size */
  256.         cp1 = &lp1 -> l_text[0];
  257.         cp2 = &lp2 -> l_text[0];
  258.         while (cp1 != &lp1 -> l_text[doto])
  259.             *cp2++ = *cp1++;
  260.         cp2 += n;
  261.         while (cp1 != &lp1 -> l_text[lp1 -> l_used])
  262.             *cp2++ = *cp1++;
  263.         lp1 -> l_bp -> l_fp = lp2;
  264.         lp2 -> l_fp = lp1 -> l_fp;
  265.         lp1 -> l_fp -> l_bp = lp2;
  266.         lp2 -> l_bp = lp1 -> l_bp;
  267.         lp2 -> l_file_offset = lp1 -> l_file_offset;
  268.         free ((char *) lp1);
  269.         }
  270.     else
  271.         {
  272.     /* Easy: in place   */
  273.         lp2 = lp1;              /* Pretend new line */
  274.         lp2 -> l_used += n;
  275.         cp2 = &lp1 -> l_text[lp1 -> l_used];
  276.         cp1 = cp2 - n;
  277.         while (cp1 != &lp1 -> l_text[doto])
  278.             *--cp2 = *--cp1;
  279.         }
  280.  
  281.     for (i = 0; i < n; ++i)     /* Add the characters   */
  282.         lp2 -> l_text[doto + i] = c;
  283.     wp = wheadp;                /* Update windows   */
  284.     while (wp != NULL)
  285.         {
  286.         if (wp -> w_linep == lp1)
  287.             {
  288.             wp -> w_linep = lp2;
  289.             }
  290.  
  291.         if (wp -> w_dotp == lp1)
  292.             {
  293.             wp -> w_dotp = lp2;
  294.             if (wp == curwp || wp -> w_doto > doto)
  295.                 wp -> w_doto += n;
  296.             }
  297.  
  298.         if (wp -> w_markp == lp1)
  299.             {
  300.             wp -> w_markp = lp2;
  301.             if (wp -> w_marko > doto)
  302.                 wp -> w_marko += n;
  303.             }
  304.  
  305.         wp = wp -> w_wndp;
  306.         }
  307.     l_fix_up (curwp -> w_dotp);   /* re-adjust file offsets */
  308.     return (TRUE);
  309. }
  310.  
  311.  
  312. /*
  313. * This function deletes "n" bytes,
  314. * starting at dot. It understands how to deal
  315. * with end of lines, etc. It returns TRUE if all
  316. * of the characters were deleted, and FALSE if
  317. * they were not (because dot ran into the end of
  318. * the buffer). The "kflag" is TRUE if the text
  319. * should be put in the kill buffer.
  320. */
  321. bool ldelete (n, kflag)
  322. {
  323.     register char  *cp1;
  324.     register char  *cp2;
  325.     register    LINE * dotp;
  326.     register short  doto, l_size, prev_size;
  327.     register    WINDOW * wp;
  328.  
  329. #if RUNCHK
  330.     /* check that buffer size can be changed */
  331.     if (curbp -> b_flag & BFSLOCK)
  332.     {
  333.         writ_echo (ERR_lock_del);
  334.         return (FALSE);
  335.     }
  336. #endif
  337.     
  338.     doto = curwp -> w_doto;
  339.  
  340.     lchange (WFMOVE);
  341.         
  342.     prev_size = 0;
  343.     while (((l_size = curwp -> w_dotp -> l_used) - doto) < n)
  344.         {
  345.         /* break out when there are no more lines to delete */
  346.         if (l_size == prev_size)
  347.             break;
  348.         /* Must merge the two lines. */
  349.         if (ldelnewline_p () == FALSE)
  350.             return (FALSE);
  351.         prev_size = l_size;
  352.         }
  353.  
  354.     dotp = curwp -> w_dotp;
  355.  
  356.     /* if at the end of the buffer then delete nothing */
  357.     if (doto >= dotp -> l_used)
  358.         {
  359.         l_fix_up (dotp);    /* re-adjust file offsets */
  360.         return (TRUE);
  361.         }
  362.     cp1 = &dotp -> l_text[doto];/* Scrunch text.    */
  363.     cp2 = cp1 + n;
  364.  
  365.     /* put stuff to delete into the kill buffer */
  366.     if (kflag != FALSE)
  367.         {
  368.         /* Kill?        */
  369.         while (cp1 != cp2)
  370.             {
  371.             if (kinsert (*cp1) == FALSE)
  372.                 return (FALSE);
  373.             ++cp1;
  374.             }
  375.  
  376.         cp1 = &dotp -> l_text[doto];
  377.         }
  378.         /* kill bytes in the current line */
  379.     while (cp2 < &dotp -> l_text[dotp -> l_used])
  380.         *cp1++ = *cp2++;
  381.  
  382.     dotp -> l_used -= n;
  383.     wp = wheadp;            /* Fix windows      */
  384.     while (wp != NULL)
  385.         {
  386.         if (wp -> w_dotp == dotp && wp -> w_doto >= doto)
  387.             {
  388.             wp -> w_doto -= n;
  389.             if (wp -> w_doto < doto)
  390.                 wp -> w_doto = doto;
  391.             }
  392.  
  393.         if (wp -> w_markp == dotp && wp -> w_marko >= doto)
  394.             {
  395.             wp -> w_marko -= n;
  396.             if (wp -> w_marko < doto)
  397.                 wp -> w_marko = doto;
  398.             }
  399.  
  400.         wp = wp -> w_wndp;
  401.         }
  402.     l_fix_up (curwp -> w_dotp);    /* re-adjust file offsets */
  403.     return (TRUE);
  404. }
  405.  
  406.  
  407. /*
  408. * Delete a newline. Join the current line
  409. * with the next line. Always allocate some extra space, if this
  410. * line was edited ones then it will probably be again. It will
  411. * go faster the second time. If the next line is the magic
  412. * header line always return TRUE; merging the last line
  413. * with the header line can be thought of as always being a
  414. * successful operation, even if nothing is done, and this makes
  415. * the kill buffer work "right". Easy cases can be done by
  416. * shuffling data around. Hard cases require that lines be moved
  417. * about in memory. Return FALSE on error and TRUE if all
  418. * looks ok. Called by "ldelete" only.
  419. */
  420. bool ldelnewline_p ()
  421. {
  422.  
  423.     register char  *cp1;
  424.     register char  *cp2;
  425.     register    LINE * lp1;
  426.     register    LINE * lp2;
  427.     register    LINE * lp3;
  428.     register    WINDOW * wp;
  429.  
  430.     lp1 = curwp -> w_dotp;
  431.     lp2 = lp1 -> l_fp;
  432.     if (lp2 == curbp -> b_linep)
  433.         {
  434.         /* At the buffer end.   */
  435.         if (lp1 -> l_used == 0) /* Blank line.      */
  436.             lfree (lp1);
  437.         return (TRUE);
  438.         }
  439.  
  440.     /* will next line fit in current line? */
  441.     if (lp2 -> l_used <= lp1 -> l_size - lp1 -> l_used)
  442.         {
  443.  
  444.         cp1 = &lp1 -> l_text[lp1 -> l_used];
  445.         cp2 = &lp2 -> l_text[0];
  446.         while (cp2 != &lp2 -> l_text[lp2 -> l_used])
  447.             *cp1++ = *cp2++;
  448.         wp = wheadp;
  449.         while (wp != NULL)
  450.             {
  451.  
  452.             if (wp -> w_linep == lp2)
  453.                 {
  454.                 wp -> w_linep = lp1;
  455.                 wp -> w_loff += lp1 -> l_used;
  456.                 }
  457.  
  458.             if (wp -> w_dotp == lp2)
  459.                 {
  460.                 wp -> w_dotp = lp1;
  461.                 wp -> w_doto += lp1 -> l_used;
  462.                 }
  463.  
  464.             if (wp -> w_markp == lp2)
  465.                 {
  466.                 wp -> w_markp = lp1;
  467.                 wp -> w_marko += lp1 -> l_used;
  468.                 }
  469.  
  470.             wp = wp -> w_wndp;
  471.             }
  472.  
  473.         lp1 -> l_used += lp2 -> l_used;
  474.         lp1 -> l_fp = lp2 -> l_fp;
  475.         lp2 -> l_fp -> l_bp = lp1;
  476.         free ((char *) lp2);
  477.         return (TRUE);
  478.         }
  479.  
  480.     /* lines too big so allocate a new one */
  481.     if ((lp3 = lalloc (lp1 -> l_used + lp2 -> l_used + NBLOCK)) == NULL)
  482.         return (FALSE);
  483.     lp3 -> l_used = lp1 -> l_used + lp2 -> l_used;  /* set to correct size */
  484.     cp1 = &lp1 -> l_text[0];
  485.     cp2 = &lp3 -> l_text[0];
  486.     while (cp1 != &lp1 -> l_text[lp1 -> l_used])
  487.         *cp2++ = *cp1++;
  488.     cp1 = &lp2 -> l_text[0];
  489.     while (cp1 != &lp2 -> l_text[lp2 -> l_used])
  490.         *cp2++ = *cp1++;
  491.     lp1 -> l_bp -> l_fp = lp3;
  492.     lp3 -> l_fp = lp2 -> l_fp;
  493.     lp2 -> l_fp -> l_bp = lp3;
  494.     lp3 -> l_bp = lp1 -> l_bp;
  495.     lp3 -> l_file_offset = lp1 -> l_file_offset;
  496.     wp = wheadp;
  497.     while (wp != NULL)
  498.         {
  499.  
  500.         if (wp -> w_linep == lp1 || wp -> w_linep == lp2)
  501.             {
  502.             wp -> w_linep = lp3;
  503.             if (wp -> w_linep == lp2)
  504.                 wp -> w_loff += lp1 -> l_used;
  505.             }
  506.  
  507.         if (wp -> w_dotp == lp1)
  508.             wp -> w_dotp = lp3;
  509.         else
  510.             if (wp -> w_dotp == lp2)
  511.                 {
  512.                 wp -> w_dotp = lp3;
  513.                 wp -> w_doto += lp1 -> l_used;
  514.                 }
  515.  
  516.         if (wp -> w_markp == lp1)
  517.             wp -> w_markp = lp3;
  518.         else
  519.             if (wp -> w_markp == lp2)
  520.                 {
  521.                 wp -> w_markp = lp3;
  522.                 wp -> w_marko += lp1 -> l_used;
  523.                 }
  524.  
  525.         wp = wp -> w_wndp;
  526.         }
  527.  
  528.     free ((char *) lp1);
  529.     free ((char *) lp2);
  530.     return (TRUE);
  531. }
  532.  
  533. /*
  534. *   Replace character at dot position.
  535. */
  536. bool    lreplace (n, c)
  537. int     n;
  538. char    c;
  539.     {
  540.     lchange (WFEDIT);
  541.     while (n--)
  542.         {
  543.         DOT_CHAR(curwp) = c & 0xff;
  544.         move_ptr (curwp, 1L, TRUE, FALSE, TRUE);
  545.         }
  546.     }   
  547.     
  548. /*
  549. * Replace plen characters before dot with argument string.
  550. */
  551. bool lrepl_str (plen, rstr, mstr)
  552.  
  553. register int    plen;           /* length to remove     */
  554. register LINE   *rstr;          /* replace string       */
  555. register LINE   *mstr;          /* mask string       */
  556. {
  557.     register    int    i;       /* used for random characters   */
  558.     register    LINE   *dotp;   /* pointer to line structure */
  559.     register    int    doto;    /* offset into line     */
  560.     register    int     rlen;   /* rplace string length */
  561.     register    char    c;      /* temp storage for char */
  562.     register    char    mask;   /* temp storage for mask */
  563.  
  564.  /* 
  565.   * make the string lengths match (either pad the line
  566.   * so that it will fit, or scrunch out the excess).
  567.   * be careful with dot's offset.
  568.   */
  569.     doto = curwp -> w_doto;
  570.     rlen = rstr -> l_used;
  571.     if (plen > rlen)
  572.         {
  573.         ldelete (plen - rlen, FALSE);
  574.         }
  575.     else if (plen < rlen)
  576.         {
  577.         if (linsert (rlen - plen, ' ') == FALSE)
  578.             return (FALSE);
  579.         }
  580.     curwp -> w_doto = doto;
  581.     dotp = curwp -> w_dotp;     /* save dot line for later */
  582.  
  583.     /* do the replacement. */
  584.     for (i = 0; i < rlen; i++)
  585.     {
  586.         c = DOT_CHAR(curwp);
  587.         mask = mstr -> l_text[i];
  588.         DOT_CHAR(curwp) = (c & mask) | (rstr -> l_text[i] & ~mask);
  589.         move_ptr (curwp, 1L, TRUE, FALSE, TRUE);
  590.     }
  591.     curwp -> w_doto = doto;
  592.     curwp -> w_dotp = dotp;
  593.     lchange (WFHARD);
  594.     return (TRUE);
  595. }
  596.  
  597.  
  598. /*
  599. * Delete all of the text
  600. * saved in the kill buffer. Called by commands
  601. * when a new kill context is being created. The kill
  602. * buffer array is released, just in case the buffer has
  603. * grown to immense size. No errors.
  604. */
  605. void kdelete ()
  606. {
  607.     if (kbufp != NULL)
  608.         {
  609.         free ((char *) kbufp);
  610.         kbufp = NULL;
  611.         kused = 0;
  612.         ksize = 0;
  613.         }
  614. }
  615.  
  616. /*
  617. * Insert a character to the kill buffer,
  618. * enlarging the buffer if there isn't any room. Always
  619. * grow the buffer in chunks, on the assumption that if you
  620. * put something in the kill buffer you are going to put
  621. * more stuff there too later. Return TRUE if all is
  622. * well, and FALSE on errors. Print a message on
  623. * errors.
  624. */
  625. bool kinsert (c)
  626. {
  627.     register char  *nbufp;
  628.     register int    i;
  629.  
  630.     if (kused == ksize)
  631.         {
  632.         if ((nbufp = malloc (ksize + KBLOCK)) == NULL)
  633.             {
  634.             writ_echo (MSG_too_m_k);
  635.             return (FALSE);
  636.             }
  637.         for (i = 0; i < ksize; ++i)
  638.             nbufp[i] = kbufp[i];
  639.         if (kbufp != NULL)
  640.             free ((char *) kbufp);
  641.         kbufp = nbufp;
  642.         ksize += KBLOCK;
  643.         }
  644.     kbufp[kused++] = c;
  645.     return (TRUE);
  646. }
  647.  
  648.  
  649. /*
  650. * This function gets characters from
  651. * the kill buffer. If the character index "n" is
  652. * off the end, it returns "-1". This lets the caller
  653. * just scan along until it gets a "-1" back.
  654. */
  655. char    kremove (n)
  656. {
  657.     if      (n >= kused)
  658.                 return (-1);
  659.     return (kbufp[n] & 0xFF);
  660. }
  661.  
  662. /*
  663. *   Line fixup.
  664. *   This fixes the 'l_file_offset' variable in
  665. *   each line structure.
  666. *   This is necessary after every change in the size
  667. *   of the buffer.
  668. */
  669. void l_fix_up (line)
  670.  
  671. LINE * line;                    /* points to buffer header line */
  672.  
  673. {
  674.     long    offset;
  675.     LINE * end_line;
  676.  
  677.     end_line = curwp -> w_bufp -> b_linep;/* header line */
  678.  
  679.     if (line == NULL)
  680.         line = end_line -> l_fp;/* point to first line */
  681.  
  682.     offset = line -> l_file_offset;/* starting offset */
  683.     offset += line -> l_used;
  684.     while ((line = line -> l_fp) != end_line)
  685.         {
  686.         line -> l_file_offset = offset;
  687.         offset += line -> l_used;
  688.         }
  689. }
  690.  
  691.  
  692.  
  693.