home *** CD-ROM | disk | FTP | other *** search
/ Media Share 9 / MEDIASHARE_09.ISO / progmisc / nfsrc21.zip / FTTEXT.C < prev    next >
C/C++ Source or Header  |  1992-10-17  |  38KB  |  1,293 lines

  1. /*
  2.  * File......: TEXT.C
  3.  * Author....: Brice de Ganahl and Steve Larsen
  4.  * CIS ID....: 76370,1532
  5.  * Date......: $Date:   17 Oct 1992 16:25:16  $
  6.  * Revision..: $Revision:   1.7  $
  7.  * Log file..: $Logfile:   C:/nanfor/src/fttext.c_v  $
  8.  * 
  9.  * This is an original work by Brice de Ganahl and Steve Larsen
  10.  * and is placed in the public domain. 
  11.  *
  12.  * Doc headers by Glenn Scott, Don Caton, and Steve Larsen
  13.  *
  14.  * Extensively revised by Steve Larsen
  15.  *
  16.  * Modification history:
  17.  * ---------------------
  18.  *
  19.  * $Log:   C:/nanfor/src/fttext.c_v  $
  20.  * 
  21.  *    Rev 1.7   17 Oct 1992 16:25:16   GLENN
  22.  * Leo cleaned up the documentation, including an errant SEEALSO
  23.  * reference.
  24.  * 
  25.  *    Rev 1.6   03 Oct 1992 02:07:38   GLENN
  26.  * Minor adjustments to file header block.
  27.  * 
  28.  *    Rev 1.5   03 Oct 1992 02:03:44   GLENN
  29.  * Major modifications by Steve Larsen, as follows:
  30.  * 
  31.  * Brice laid some wonderful groundwork with his initial release of
  32.  * these functions, however I needed more capability.  With his per-
  33.  * mission, I have made the following additions/changes to Rev. 1.4:
  34.  * 
  35.  * -  Eliminated the problem of memory for buffers being re-allocated every
  36.  *    time a file got used.
  37.  * -  Further reduced memory impact by converting from extend system memory
  38.  *    allocation techniques to virtual memory.  To accomplish this, we
  39.  *    use the Clipper v5.01 r1.29 variants of the "_v" undocumented
  40.  *    internal functions.  If these functions change in future releases, you
  41.  *    will need to locate them herein and make the appropriate changes.
  42.  * 
  43.  *    NOTE: these functions allocate and deallocate virtual memory on an
  44.  *    "as-needed" basis.  If your application makes heavy and frequent use
  45.  *    of those functions that perform a lot of buffering (ft_fInsert(),
  46.  *    ft_fDelete() and ft_fWrite()), you might consider modifying the memory
  47.  *    management scheme used herein, that is, allocate the required buffers
  48.  *    only once upon the first call to these functions, then recycle them.
  49.  * -  Added the ability to specify file open mode.
  50.  * -  Added a function to write to a record, which through a switch can either
  51.  *    over-write the current record, or insert a new one.
  52.  * -  Added functions to insert, delete and append a specified number of lines.
  53.  * -  Fixed the existing functions so that they properly handle "trailers",
  54.  *    that is, a case where the last chars in a file are not CRLF delimited.
  55.  * -  Provided checking for the possibility that the file might be terminated
  56.  *    with ^Z (1Ah), if so, ignoring it (providing consistency with non-^Z
  57.  *    terminated files).  This only occurs on the last record of a file.
  58.  * -  Eliminated a potential problem if one were to issue an ft_fUse() prior
  59.  *    actually opening any files.
  60.  * -  Replaced the original C parsing logic to determine the end-of-line (CRLF)
  61.  *    with an optimized assembler routine.  This bypassed a significant
  62.  *    performance hit.
  63.  * -  The original header (FTTEXT.h) file in now incorporated in this one file.
  64.  *    This is not necessarily an enhancement, more like laziness.
  65.  * -  Provided the (followup) author with his very first C experience!
  66.  * 
  67.  *    Steve Larsen, Dec. 7, 1991   CIS 76370,1532
  68.  * 
  69.  * -  Function changes/additions (refer to the individual doc headers for
  70.  *    details):
  71.  * 
  72.  *    FT_FSELECT( [ < nArea  > ] )                 -> nArea
  73.  *    FT_FUSE(    [ < cFile  > ][, < nMode >   ] ) -> nHandle | NIL
  74.  *    FT_FWRITELN(  < cData  >  [, < lInsert > ] ) -> NIL
  75.  *    FT_FINSERT( [ < nLines > ] )                 -> NIL
  76.  *    FT_FDELETE( [ < nLines > ] )                 -> NIL
  77.  *    FT_FAPPEND( [ < nLines > ] )                 -> NIL
  78.  * 
  79.  *    Internal Steve Larsen revisions:
  80.  * 
  81.  *     12/07/91  Original rework
  82.  *     02/13/92  Fixed _findeol(), FT_FREADLN() and FT_FGOBOT() to
  83.  *               better handle files with CRLF, LF, ^Z or nothing
  84.  *               at the EOF.  Previously, under some conditions the
  85.  *               last record was chopped by a character, depending
  86.  *               on the last character(s).
  87.  *     05/02/92  Fixed buffering and VMM allocation problem with
  88.  *               FT_FGOBOT().
  89.  *     08/26/92  Correcting problem when appending blank lines to an
  90.  *               empty file (ft_fAppend() and ft_fWriteLn()).
  91.  * 
  92.  * 
  93.  *    Rev 1.4   17 Aug 1991 15:31:08   GLENN
  94.  * Don Caton fixed some spelling errors in the doc
  95.  * 
  96.  *    Rev 1.3   15 Aug 1991 23:08:36   GLENN
  97.  * Forest Belt proofread/edited/cleaned up doc
  98.  * 
  99.  *    Rev 1.2   29 Apr 1991 08:02:12   GLENN
  100.  * Minor adjustments to documentation block
  101.  * 
  102.  *    Rev 1.1   29 Apr 1991 08:00:26   GLENN
  103.  * ft_flastrec() -- name was longer than 10 characters so linkers couldn't
  104.  * find the symbol.  Just hacked off the last "c" so it is really 
  105.  * ft_flastre().  Sorry, folks.  -- Glenn
  106.  * 
  107.  *    Rev 1.0   01 Apr 1991 01:02:48   GLENN
  108.  * Nanforum Toolkit
  109.  * 
  110.  */
  111.  
  112. /*  Notes:
  113.  
  114.      The Clipper internal functions used seem to be stable across
  115.      versions but nothing is guaranteed.  These functions begin
  116.      with _t, are used for file I/O, and are compatible with their
  117.      ANSI counterparts (just strip the _t and you have the ANSI name).
  118.      See text.h for the prototypes.
  119.  
  120.      This revision utilizes the in-line assembler feature found in MSC
  121.      6.0.  If compiling with TurboC substitute "_asm" with "asm".
  122.  
  123.      I compile these functions with the following MicroSoft C parameters:
  124.  
  125.           cl  /c /AL /Od /Zl /Zi /FPa /Gs /W3 text.c
  126.  
  127.      Note that the /Od defeats optimization and is necessary only for
  128.      compatibility with Blinker, Warplink, etc.  If you are not overlaying
  129.      this code you may want to change this to /Oalt.  Likewise, the
  130.      /Zi is for symbolic debugging info which you will want to omit in
  131.      any final compiles.
  132.  
  133.      Some sample Clipper code which would use these functions is listed
  134.      below.  It will print out the contents of this file.
  135.  
  136.               ft_fuse( "text.c" )
  137.               do while !ft_feof()
  138.                  ? ft_freadln()
  139.                  ft_fskip()
  140.               enddo
  141.               ft_fuse()
  142.  
  143.  
  144. */
  145.  
  146. /* up this number if you need more than 10 text file areas */
  147.  
  148. #define TEXT_WORKAREAS 10
  149.  
  150. #include "extend.h"
  151. #include "stdio.h"
  152. #include "share.h"
  153. #include "fcntl.h"
  154.  
  155. #define b_size     1024
  156. #define c_size     4096
  157.  
  158. #ifndef SIZE_T
  159.    #define SIZE_T
  160.    typedef unsigned int size_t;
  161. #endif
  162.  
  163. void pascal ft_fseek( void );
  164. void pascal ft_fuse( void );
  165. void pascal ft_fselect( void );
  166. void pascal ft_fgotop( void );
  167. void pascal ft_frecno( void );
  168. void pascal ft_fgobot( void );
  169. void pascal ft_fskip( void );
  170. void pascal ft_freadln( void );
  171. void pascal ft_flastre( void );
  172. void pascal ft_feof( void );
  173. void pascal ft_fgoto( void );
  174. void pascal ft_fwritel( void );
  175. void pascal ft_fdelete( void );
  176. void pascal ft_fappend( void );
  177.  
  178. int _findeol( char *buf, int buf_len );    /* in-line ASM */
  179. int _findbol( char *buf, int buf_len );    /* in-line ASM */
  180. /*void _ftwrite( char *buf, int insert );   (replaced below) */
  181. long _filewrite( long read1, long read2, long end1, long end2,
  182.                  char *c, char *c2 );
  183.  
  184. long _ft_skip( int recs );
  185.  
  186. extern int  _tclose( int );
  187. extern int  _tcreat( char*, int );
  188. extern int  _terror;
  189. extern long _tlseek( int, long, int );
  190. extern int  _topen( char*, int );
  191. extern int  _tread( int, char*, int );
  192. extern int  _twrite( int, char*, int );
  193. extern int  _tcommit( int );
  194. /* extern int  strlen( char* ); */
  195. extern int  _vAlloc( int, int );
  196. extern char *_vLock( int );
  197. extern void _vUnLock( int );
  198. extern void _vFree( int );
  199.  
  200. static long recno[TEXT_WORKAREAS];
  201. static long offset[TEXT_WORKAREAS];
  202. static int  handles[TEXT_WORKAREAS];
  203. static int  area = 0;
  204. static long last_rec[TEXT_WORKAREAS];
  205. static long last_off[TEXT_WORKAREAS];
  206. static long lastbyte[TEXT_WORKAREAS];
  207. static int  isEof[TEXT_WORKAREAS];
  208.  
  209.  
  210. /*  $DOC$
  211.  *  $FUNCNAME$
  212.  *     FT_FUSE()
  213.  *  $CATEGORY$
  214.  *     File I/O
  215.  *  $ONELINER$
  216.  *     Open or close a text file for use by the FT_F* functions
  217.  *  $SYNTAX$
  218.  *     FT_FUSE( [ <cFile> ] [, <nMode> ] ) -> nHandle | NIL
  219.  *  $ARGUMENTS$
  220.  *     <cFile> is the text file you want to open.  If not specified,
  221.  *     the file currently open, if any, will be closed.
  222.  *
  223.  *     <nMode> is the open mode for the file.  Please refer to the
  224.  *     discussion of open modes under FOPEN() in the Clipper manual
  225.  *     and FILEIO.CH for a list of allowable open modes.  If not
  226.  *     specified, the file will be opened with a mode of
  227.  *     FO_READ + FO_SHARED (64).
  228.  *
  229.  *  $RETURNS$
  230.  *     If <cFile> is passed and the file is opened successfully, an
  231.  *     integer containing the file handle.  If the file cannot be
  232.  *     opened, -1 will be returned.
  233.  *
  234.  *     If FT_FUSE() is called without any arguments, it will close the
  235.  *     text file in the current "text area" and return NIL.
  236.  *  $DESCRIPTION$
  237.  *     The FT_F*() file functions are for reading text files, that is,
  238.  *     files where each line (record) is delimited by a CRLF pair.
  239.  *
  240.  *     Each file is opened in its own "workarea", similar to the concept
  241.  *     use by dbf files.  As provided, a maximum of 10 files (in 10
  242.  *     workareas) can be opened (assuming there are sufficient file
  243.  *     handles available).  That number may be increased by modifying
  244.  *     the #define TEXT_WORKAREAS in the C source code and recompiling.
  245.  *  $EXAMPLES$
  246.  *     FT_FUSE( "text.c" )      // open text file
  247.  *     DO WHILE !FT_FEOF()
  248.  *        ? FT_FREADLN()
  249.  *        FT_FSKIP()
  250.  *     ENDDO
  251.  *     FT_FUSE()                // close file
  252.  *  $SEEALSO$
  253.  *     FT_FUSE() FT_FSELECT()
  254.  *  $END$
  255.  */
  256.  
  257. void pascal ft_fuse()
  258. {
  259.    int attr = ISNUM( 2 ) ? _parni(2) : O_RDONLY|SH_DENYNO|O_BINARY ;
  260.  
  261.    if ( ISCHAR(1) ) {
  262.       handles[area] = _topen( _parc(1), attr ) ;
  263.  
  264.       offset[area] = 0 ;
  265.       recno[area] = 1;
  266.       lastbyte[area] = _tlseek( handles[area], 0L, SEEK_END );
  267.       _retni( handles[area] );
  268.    }
  269.    else {
  270.       if ( handles[area] != 0 ) {
  271.          _tclose( handles[area] );
  272.          _retni(1);
  273.          recno[area] = 0L;
  274.          offset[area] = 0L;
  275.          handles[area] = 0;
  276.          last_rec[area] = 0L;
  277.          last_off[area] = 0L;
  278.          lastbyte[area] = 0L;
  279.          isEof[area] = 0;
  280.       }
  281.    }
  282. }
  283.  
  284.  
  285. /*  $DOC$
  286.  *  $FUNCNAME$
  287.  *     FT_FSELECT()
  288.  *  $CATEGORY$
  289.  *     File I/O
  290.  *  $ONELINER$
  291.  *     Select a text file workarea
  292.  *  $SYNTAX$
  293.  *     FT_FSELECT( [ <nArea> ] ) -> nArea
  294.  *  $ARGUMENTS$
  295.  *     <nArea> is the text file workarea to select.
  296.  *  $RETURNS$
  297.  *     The current selected text file area.
  298.  *
  299.  *  $DESCRIPTION$
  300.  *     This function selects a text file "workarea" from 1 to 10.  A
  301.  *     file may or may not be open in the selected area.
  302.  *
  303.  *     Passing 0 for <nArea> selects the next available workarea, similar
  304.  *     to Clipper's SELECT 0 command.
  305.  *
  306.  *     Each file is opened in its own "workarea", similar to the concept
  307.  *     used by dbf files.  As provided, a maximum of 10 files (in 10
  308.  *     workareas) can be opened (assuming there are sufficient file
  309.  *     handles available).  That number may be increased by modifying
  310.  *     the #define TEXT_WORKAREAS in the C source code and recompiling.
  311.  *
  312.  *     All the FT_F*() file functions operate on the file in the currently
  313.  *     selected text file workarea.
  314.  *
  315.  *     Text file workareas are separate from and independent of Clipper's
  316.  *     database workareas.
  317.  *  $EXAMPLES$
  318.  *     FT_FSELECT(1)
  319.  *     nFile1 := FT_FUSE( "temp.c" )
  320.  *     ? FT_FLASTRE()                 // no. of lines in temp.c
  321.  *     FT_FSELECT(2)
  322.  *     nFile2 := FT_FUSE( "temp.h" )
  323.  *     ? FT_FLASTRE()                 // no. of lines in temp.h
  324.  *  $SEEALSO$
  325.  *     FT_FUSE()
  326.  *  $END$
  327.  */
  328.  
  329. void pascal ft_fselect()
  330. {
  331.    if ( ISNUM(1) ) {
  332.       area = _parni(1) - 1;
  333.       if ( area == -1 ) {
  334.          for ( area = 0; area < TEXT_WORKAREAS - 1; area++ ) {
  335.             if ( handles[area] == 0 ) {
  336.                 break;
  337.             }
  338.          }
  339.       }
  340.    }
  341.    _retni( area + 1 );
  342. }
  343.  
  344. /*  $DOC$
  345.  *  $FUNCNAME$
  346.  *     FT_FGOTOP()
  347.  *  $CATEGORY$
  348.  *     File I/O
  349.  *  $ONELINER$
  350.  *     Go to the first record in a text file
  351.  *  $SYNTAX$
  352.  *     FT_FGOTOP() -> NIL
  353.  *  $ARGUMENTS$
  354.  *     None
  355.  *  $RETURNS$
  356.  *     NIL
  357.  *  $DESCRIPTION$
  358.  *     This function moves the record pointer to the first record
  359.  *     in the currently selected text file workarea.
  360.  *
  361.  *     A text file "record" is a line of text terminated by a CRLF pair.
  362.  *  $EXAMPLES$
  363.  *     FT_FUSE( "text.c" )      // open text file
  364.  *     DO WHILE !FT_FEOF()
  365.  *        ? FT_FREADLN()        // read thru file
  366.  *        FT_FSKIP()
  367.  *     ENDDO
  368.  *     FT_FGOTOP()              // go back to top
  369.  *     ? FT_FRECNO()            // 1
  370.  *  $SEEALSO$
  371.  *     FT_FSELECT() FT_FUSE() FT_FRECNO() FT_FGOBOT()
  372.  *  $END$
  373.  */
  374.  
  375. void pascal ft_fgotop()
  376. {
  377.  
  378.    offset[area] = 0L;
  379.    recno[area] = 1L;
  380.  
  381. }
  382.  
  383.  
  384. /*  $DOC$
  385.  *  $FUNCNAME$
  386.  *     FT_FRECNO()
  387.  *  $CATEGORY$
  388.  *     File I/O
  389.  *  $ONELINER$
  390.  *     Return the current record number of a text file
  391.  *  $SYNTAX$
  392.  *     FT_FRECNO() -> nRecNo
  393.  *  $ARGUMENTS$
  394.  *     None
  395.  *  $RETURNS$
  396.  *     The current record number of a text file or 0 if no file is open.
  397.  *  $DESCRIPTION$
  398.  *     This function returns the current record number of the file open
  399.  *     in the currently selected text file workarea.
  400.  *
  401.  *     A text file "record" is a line of text terminated by a CRLF pair.
  402.  *  $EXAMPLES$
  403.  *     FT_FUSE( "text.c" )      // open text file
  404.  *     DO WHILE !FT_FEOF()
  405.  *        ? FT_FREADLN()        // read thru file
  406.  *        FT_FSKIP()
  407.  *     ENDDO
  408.  *     FT_FGOTOP()              // go back to top
  409.  *     ? FT_FRECNO()            // 1
  410.  *  $SEEALSO$
  411.  *      FT_FSELECT() FT_FUSE() FT_FGOTOP() FT_FGOBOT()
  412.  *  $END$
  413.  */
  414.  
  415.  
  416. void pascal ft_frecno()
  417. {
  418.    _retnl( recno[area] );
  419. }
  420.  
  421.  
  422. /*  $DOC$
  423.  *  $FUNCNAME$
  424.  *     FT_FGOBOT()
  425.  *  $CATEGORY$
  426.  *     File I/O
  427.  *  $ONELINER$
  428.  *     Go to the last record in a text file
  429.  *  $SYNTAX$
  430.  *     FT_FGOBOT() -> NIL
  431.  *  $ARGUMENTS$
  432.  *     None
  433.  *  $RETURNS$
  434.  *     NIL
  435.  *  $DESCRIPTION$
  436.  *     This function moves the record pointer to the last record of the
  437.  *     file in the currently selected text file workarea.
  438.  *
  439.  *     A text file "record" is a line of text terminated by a CRLF pair.
  440.  *  $EXAMPLES$
  441.  *     // read last line
  442.  *     FT_FUSE( "text.c" )
  443.  *     FT_FGOBOT()
  444.  *     ? FT_FREADLN()
  445.  *  $SEEALSO$
  446.  *     FT_FSELECT() FT_FUSE() FT_FGOTOP() FT_FRECNO() FT_FREADLN()
  447.  *  $END$
  448.  */
  449.  
  450.  
  451. void pascal ft_fgobot()
  452. {
  453.  
  454.    int x;
  455.    int len, blen;
  456.    long loc;
  457.    int   c_ptr;
  458.    char * c, * d;
  459.  
  460.    if ( last_rec[area] != 0 ) {
  461.       recno[area] = last_rec[area];
  462.       offset[area] = last_off[area];
  463.    }
  464.    else {
  465.  
  466.       c_ptr = _vAlloc( c_size, 0 );
  467.       d = _vLock( c_ptr );
  468.  
  469.  
  470.       loc = 0L;
  471.  
  472.       do {
  473.          c = d;
  474.  
  475.          _tlseek( handles[area], offset[area], SEEK_SET );
  476.          len = _tread(  handles[area], c, c_size );
  477.                  blen = len;
  478.          loc = offset[area];
  479.          do {
  480.  
  481.             x = _findeol( c, len );
  482.             if ( ( x == len ) || ( ( x + 1 ) == len ) ) {
  483.                 break;
  484.             }
  485.             c   += x + 2;
  486.             len -= ( x + 2 );
  487.             recno[area]++;
  488.             loc += x + 2;
  489.          } while ( ( len > 0 ) );
  490.  
  491.          offset[area] = loc;
  492.  
  493.       } while ( blen == c_size );
  494.  
  495.       offset[area]   = loc;
  496.       last_rec[area] = recno[area];
  497.       last_off[area] = offset[area];
  498.             _vUnLock( c_ptr );
  499.       _vFree( c_ptr );
  500.    }
  501. }
  502.  
  503.  
  504. /*  $DOC$
  505.  *  $FUNCNAME$
  506.  *     FT_FSKIP()
  507.  *  $CATEGORY$
  508.  *     File I/O
  509.  *  $ONELINER$
  510.  *     Move the record pointer to a new position in a text file
  511.  *  $SYNTAX$
  512.  *     FT_FSKIP( [ <nLines> ] ) -> NIL
  513.  *  $ARGUMENTS$
  514.  *     <nLines> is the number of lines to skip.  Defaults to 1 if
  515.  *     not specified.
  516.  *  $RETURNS$
  517.  *     NIL
  518.  *  $DESCRIPTION$
  519.  *     This function moves the text file record pointer, similar to
  520.  *     the CLIPPER SKIP command.
  521.  *
  522.  *     A text file "record" is a line of text terminated by a CRLF pair.
  523.  *  $EXAMPLES$
  524.  *     // display each record of a text file
  525.  *     FT_FUSE( "text.c" )
  526.  *     DO WHILE ! FT_FEOF()
  527.  *        ? FT_FREADLN()
  528.  *        FT_FSKIP()
  529.  *     ENDDO
  530.  *  $SEEALSO$
  531.  *     FT_FRECNO() FT_FGOTOP()
  532.  *  $END$
  533.  */
  534.  
  535. void pascal ft_fskip( void )
  536. {
  537.  
  538.    if ( ISNUM(1) )
  539.       _ft_skip( _parni(1) );
  540.    else
  541.       _ft_skip(1);
  542.  
  543. }
  544.  
  545.  
  546. static long _ft_skip( int recs )
  547. {
  548.  
  549.    int x;
  550.    long read_pos;
  551.    size_t len;
  552.    long y;
  553.    int b_ptr = _vAlloc( b_size, 0 );
  554.    char *b   = _vLock( b_ptr );
  555.  
  556.    if ( recs > 0 ) {
  557.       for (y = 0; y < recs; y++ ) {
  558.          _tlseek( handles[area], offset[area], SEEK_SET );
  559.          len = _tread( handles[area], b, b_size );
  560.  
  561.          x = _findeol( b, len );
  562.          if (( x != (int)len ) && ( (offset[area] + x + 2) < lastbyte[area] )) {
  563.             isEof[area] = FALSE;
  564.             offset[area] += (x + 2);
  565.             recno[area]++;
  566.          }
  567.          else
  568.             isEof[area] = TRUE;
  569.       }
  570.    }
  571.    else {
  572.       recs = -recs;
  573.       isEof[area] = FALSE;
  574.  
  575.       if ( (recno[area] - recs) >= 1 )      // <--- reversed if condition
  576.       {                                     // <--- added opening brace
  577.          for (y = recs; y > 0; y-- ) {
  578.             if ( offset[area] - b_size < 0L ) {
  579.                read_pos = 0;
  580.                len = (size_t)offset[area];
  581.             }
  582.             else {
  583.                read_pos = (size_t)(offset[area] - b_size);
  584.                len = b_size;
  585.             }
  586.  
  587.             _tlseek( handles[area], read_pos, SEEK_SET );
  588.             len = _tread( handles[area], b, len );
  589.  
  590.             x = _findbol( b, len-3 ) ;
  591.  
  592.             if ( x < 0 ) {
  593.                offset[area] = 0;
  594.                recno[area] = 1;
  595.             }
  596.             else {
  597.                offset[area] = read_pos + x + 2;
  598.                recno[area]--;
  599.             }
  600.          }
  601.       }                                     // <--- added closing brace
  602.             else {
  603.                 offset[area] = 0;
  604.                 recno[area]  = 1;
  605.             }
  606.    }
  607.  
  608.      _vUnLock( b_ptr );
  609.    _vFree( b_ptr );
  610.    return ( recno[area] );
  611. }
  612.  
  613.  
  614. /*  $DOC$
  615.  *  $FUNCNAME$
  616.  *     FT_FREADLN()
  617.  *  $CATEGORY$
  618.  *     File I/O
  619.  *  $ONELINER$
  620.  *     Read a line from the currently selected text file
  621.  *  $SYNTAX$
  622.  *     FT_FREADLN() -> cLine
  623.  *  $ARGUMENTS$
  624.  *     None
  625.  *  $RETURNS$
  626.  *     A string containing the current record in a text file.
  627.  *  $DESCRIPTION$
  628.  *     This function returns a line of text read from the file in the
  629.  *     currently selected text file workarea.  Text lines are delimited
  630.  *     with a CRLF pair.  The record pointer is not moved.
  631.  *
  632.  *     A text file "record" is a line of text terminated by a CRLF pair.
  633.  *  $EXAMPLES$
  634.  *     // display each record of a text file
  635.  *     FT_FUSE( "text.c" )
  636.  *     DO WHILE ! FT_FEOF()
  637.  *        ? FT_FREADLN()
  638.  *        FT_FSKIP()
  639.  *     ENDDO
  640.  *  $SEEALSO$
  641.  *     FT_FUSE() FT_FWRITELN() FT_FRECNO() FT_FGOTOP()
  642.  *  $END$
  643.  */
  644.  
  645.  
  646. void pascal ft_freadln()
  647. {
  648.  
  649.    int x;
  650.    int read;
  651.    int b_ptr = _vAlloc( b_size, 0 );
  652.    char *b   = _vLock( b_ptr );
  653.  
  654.    _tlseek( handles[area], offset[area], SEEK_SET );
  655.    read = (int) _tread( handles[area], b, b_size );
  656.  
  657.    x = _findeol( b, read );
  658.  
  659.    _retclen( b, x );
  660.  
  661.      _vUnLock( b_ptr );
  662.    _vFree( b_ptr );
  663. }
  664.  
  665. /*  $DOC$
  666.  *  $FUNCNAME$
  667.  *     FT_FDELETE()
  668.  *  $CATEGORY$
  669.  *     File I/O
  670.  *  $ONELINER$
  671.  *     Deletes a line from the currently selected text file
  672.  *  $SYNTAX$
  673.  *     FT_FDELETE( [ < nLines > ] ) -> NIL
  674.  *  $ARGUMENTS$
  675.  *     <nLines> is the number of lines to be eliminated, beginning with
  676.  *     the current record position.
  677.  *
  678.  *     If <nLines> is omitted, the current record is deleted only.
  679.  *
  680.  *  $RETURNS$
  681.  *     NIL
  682.  *  $DESCRIPTION$
  683.  *     This function deletes one or several lines of text from the file
  684.  *     in the currently selected text file workarea.  Text lines are
  685.  *     delimited with a CRLF pair.  The record pointer is not moved.
  686.  *  $EXAMPLES$
  687.  *     // delete the next 4 lines from a file
  688.  *     FT_FUSE( "test.txt" )
  689.  *
  690.  *     FT_FDELETE( 4 )
  691.  *  $SEEALSO$
  692.  *     FT_FAPPEND() FT_FRECNO() FT_FINSERT()
  693.  *  $END$
  694.  */
  695.  
  696. void pascal ft_fdelete( )
  697. {
  698.    int no_lines = ( ISNUM( 1 ) ? _parni( 1 ) : 1 );
  699.    long read1;
  700.    long read2;
  701.    long end1;
  702.    long end2;
  703.    long cur_rec = recno[area];
  704.    long cur_off = offset[area];
  705.  
  706.    int b_ptr   = _vAlloc( b_size, 0 );
  707.    int c_ptr   = _vAlloc( c_size, 0 );
  708.    int c2_ptr  = _vAlloc( c_size, 0 );
  709.    char  *b    = _vLock( b_ptr);
  710.    char  *c    = _vLock( c_ptr);
  711.    char  *c2   = _vLock( c2_ptr);
  712.  
  713. /* save address to current record ( first record to be deleted ) */
  714.  
  715.    end1 = offset[area] ;
  716.  
  717. /* skip over deleted records, point to first 'to be retained' record */
  718.  
  719.    _ft_skip( no_lines ) ;
  720.    _tlseek( handles[area], offset[area], SEEK_SET );
  721.  
  722. /* save two buffers' worth of data */
  723.  
  724.    read1 = _tread( handles[area], c, c_size );   /* now read in a big glob */
  725.    read2 = _tread( handles[area], c2, c_size );   /* now read in a big glob */
  726.    end2  = offset[area] + read1 + read1;
  727.  
  728.    end1 = _filewrite( read1, read2, end1, end2, c, c2 ); /* loop to write */
  729.  
  730.    _tlseek( handles[area], (int)end1, SEEK_SET );
  731.    _twrite( handles[area], c, 0 );
  732.    last_rec[area] = 0;
  733.    lastbyte[area] = _tlseek( handles[area], 0L, SEEK_END );
  734.    recno[area] = cur_rec;
  735.    offset[area]= cur_off;
  736.    _vUnLock( b_ptr );
  737.    _vUnLock( c_ptr );
  738.    _vUnLock( c2_ptr);
  739.    _vFree( b_ptr );
  740.    _vFree( c_ptr );
  741.    _vFree( c2_ptr);
  742. }
  743.  
  744.  
  745. /*  $DOC$
  746.  *  $FUNCNAME$
  747.  *     FT_FINSERT()
  748.  *  $CATEGORY$
  749.  *     File I/O
  750.  *  $ONELINER$
  751.  *     Inserts a line in the currently selected text file
  752.  *  $SYNTAX$
  753.  *     FT_FINSERT( [ < nLines > ] ) -> NIL
  754.  *  $ARGUMENTS$
  755.  *     <nLines> is the number of lines that should be inserted at the
  756.  *     current record position.
  757.  *
  758.  *     If <nLines> is omitted, one record is inserted.
  759.  *
  760.  *  $RETURNS$
  761.  *     NIL
  762.  *  $DESCRIPTION$
  763.  *     This function inserts a line of text in the file in the currently
  764.  *     selected text file workarea.  Text lines are delimited with a
  765.  *     CRLF pair.  The record pointer is not moved.
  766.  *
  767.  *     An optional parameter allows multiple insertions to take place
  768.  *     with a single call to FT_FINSERT().
  769.  *
  770.  *     A text file "record" is a line of text terminated by a CRLF pair.
  771.  *     Each line inserted with this function will be empty.
  772.  *  $EXAMPLES$
  773.  *     // add a blank line of text to a file
  774.  *     FT_FUSE( "test.txt" )
  775.  *
  776.  *     FT_FINSERT()
  777.  *  $SEEALSO$
  778.  *     FT_FAPPEND() FT_FRECNO() FT_FDELETE() FT_FLASTRE()
  779.  *  $END$
  780.  */
  781.  
  782. void pascal ft_finsert( )
  783. {
  784.    int no_lines = ( ISNUM( 1 ) ? _parni( 1 ) : 1 );
  785.    long read1;
  786.    long read2;
  787.    long end1;
  788.    long end2;
  789.    char crlf[] = { (char)0x0D, (char)0x0A };
  790.  
  791.    int b_ptr   = _vAlloc( b_size, 0 );
  792.    int c_ptr   = _vAlloc( c_size, 0 );
  793.    int c2_ptr  = _vAlloc( c_size, 0 );
  794.    char  *b    = _vLock( b_ptr);
  795.    char  *c    = _vLock( c_ptr);
  796.    char  *c2   = _vLock( c2_ptr);
  797.  
  798. /* find end of first record to be replaced */
  799.  
  800.    end1 = _tlseek( handles[area], offset[area], SEEK_SET );
  801.  
  802. /* save two buffers' worth of data from current record */
  803.  
  804.    read1 = _tread( handles[area], c, c_size );   /* now read in a big glob */
  805.    read2 = _tread( handles[area], c2, c_size );   /* now read in a big glob */
  806.    end2  = end1 + read1 + read2;
  807.  
  808. /* write the new records */
  809.  
  810.    _tlseek( handles[area], (int)end1, SEEK_SET );
  811.  
  812.  
  813. /* need this loop to consider that No. lines inserted may be more than
  814.    the amount saved by the two buffers above */
  815.  
  816.    do {
  817.       _twrite( handles[area], crlf, 2 );
  818.       end1 += 2;
  819.    } while ( --no_lines );
  820.  
  821.    end1 = _filewrite( read1, read2, end1, end2, c, c2 ); /* loop to write */
  822.  
  823.    lastbyte[area] = _tlseek( handles[area], (int)end1, SEEK_SET );
  824.    last_rec[area] = 0L;
  825.    _twrite( handles[area], c, 0 );
  826.    _vUnLock( b_ptr );
  827.    _vUnLock( c_ptr );
  828.    _vUnLock( c2_ptr);
  829.    _vFree( b_ptr );
  830.    _vFree( c_ptr );
  831.    _vFree( c2_ptr);
  832. }
  833.  
  834. /*  $DOC$
  835.  *  $FUNCNAME$
  836.  *     FT_FAPPEND()
  837.  *  $CATEGORY$
  838.  *     File I/O
  839.  *  $ONELINER$
  840.  *     Appends a line to the currently selected text file
  841.  *  $SYNTAX$
  842.  *     FT_FAPPEND( [ < nLines > ] ) -> NIL
  843.  *  $ARGUMENTS$
  844.  *     <nLines> is the number of lines that should be appended to the
  845.  *     end of the currently selected text file.
  846.  *
  847.  *     If <nLines> is omitted, one record is appended.
  848.  *
  849.  *  $RETURNS$
  850.  *     NIL
  851.  *  $DESCRIPTION$
  852.  *     This function appends a line of text to the file in the currently
  853.  *     selected text file workarea.  Text lines are delimited with a
  854.  *     CRLF pair.  The record pointer is moved to the last appended
  855.  *     record.
  856.  *
  857.  *     Multiple lines may be appended with one call to FT_FAPPEND().
  858.  *
  859.  *     A text file "record" is a line of text terminated by a CRLF pair.
  860.  *     Each line appended with this function will be empty.
  861.  *
  862.  *     NOTE:  Occasionally a text file may contain a non-CRLF terminated
  863.  *     line, at the end of the file ("stragglers").  This function assumes
  864.  *     these stragglers to be the last line of the file, and begins
  865.  *     appending the new lines after this line.  In other words, if the
  866.  *     last line in the text file is not terminated with a CRLF pair prior
  867.  *     to calling FT_FAPPEND(), the function will terminate that last line
  868.  *     before appending any new lines.
  869.  *
  870.  *  $EXAMPLES$
  871.  *     // add a blank line of text to a file
  872.  *     FT_FUSE( "test.txt" )
  873.  *
  874.  *     ?FT_FRECNO()           // displays 5
  875.  *
  876.  *     FT_FAPPEND()
  877.  *
  878.  *     ?FT_FRECNO()           // displays 6
  879.  *  $SEEALSO$
  880.  *     FT_FRECNO() FT_FDELETE() FT_FINSERT() FT_FLASTRE()
  881.  *  $END$
  882.  */
  883.  
  884. void pascal ft_fappend( )
  885. {
  886.    int no_lines = ( ISNUM( 1 ) ? _parni( 1 ) : 1 );
  887.    long read1;
  888.    long end1;
  889.    int x;
  890.    char crlf[] = { (char)0x0D, (char)0x0A };
  891.  
  892.    int b_ptr   = _vAlloc( b_size, 0 );
  893.    char  *b    = _vLock( b_ptr);
  894.  
  895. /* go to end of file */
  896.  
  897.    ft_fgobot();
  898.  
  899. /* find end of record */
  900.  
  901.    end1 = _tlseek( handles[area], offset[area], SEEK_SET );
  902.    read1 = _tread( handles[area], b, b_size );   /* now read in a big glob */
  903.  
  904. /* determine if CRLF pair exists, if not, add one */
  905.  
  906.    if ( _findeol( b, (int)read1 ) == (int)read1 ) {
  907.       _tlseek( handles[area], lastbyte[area], SEEK_SET );
  908.       _twrite( handles[area], crlf, 2 );
  909.         no_lines--;
  910.    }
  911.  
  912. /* loop to write new lines */
  913.  
  914.    for ( x = 0; x < no_lines; x ++ ) {
  915.       _twrite( handles[area], crlf, 2 );
  916.    }
  917.       _vUnLock( b_ptr );
  918.       _vFree( b_ptr );
  919.       last_rec[area] = 0L;
  920.       ft_fgobot();
  921. }
  922.  
  923. /*  $DOC$
  924.  *  $FUNCNAME$
  925.  *     FT_FWRITELN()
  926.  *  $CATEGORY$
  927.  *     File I/O
  928.  *  $ONELINER$
  929.  *     Write a line to the currently selected text file
  930.  *  $SYNTAX$
  931.  *     FT_FWRITELN( < cData >, [ < lInsert > ] ) -> NIL
  932.  *  $ARGUMENTS$
  933.  *     <cData> is a string of data to write to the file at the current
  934.  *      record position.
  935.  *
  936.  *     <lInsert> is a logical indicating whether the contents
  937.  *     of the current record are to be preserved, that is, if lInsert
  938.  *     evaluates to .T., the a new record is inserted at the current
  939.  *     position.  The current record then is pushed down to FT_FRECNO()+1.
  940.  *
  941.  *     If lInsert is .F. or omitted, the current record is replaced by
  942.  *     cData.
  943.  *
  944.  *  $RETURNS$
  945.  *     NIL
  946.  *  $DESCRIPTION$
  947.  *     This function writes a line of text to the file in the currently
  948.  *     selected text file workarea.  Text lines are delimited with a
  949.  *     CRLF pair.  The record pointer is not moved.
  950.  *
  951.  *     The contents of the current record are updated to reflect the new
  952.  *     new line written, unless the Insert option is selected.
  953.  *
  954.  *     Writing a null string has the effect of clearing the current line
  955.  *     if in overstrike mode, else inserting a new line (same as
  956.  *     FT_FINSERT()).
  957.  *
  958.  *     A text file "record" is a line of text terminated by a CRLF pair.
  959.  *  $EXAMPLES$
  960.  *     // write a line of text to a file
  961.  *     FT_FUSE( "config.sys" )
  962.  *     DO WHILE UPPER( FT_FREADLN() ) != "FILES=" .AND. !F_FEOF()
  963.  *        FT_FSKIP()
  964.  *     ENDDO
  965.  *
  966.  *     FT_FWRITELN( "FILES=30", FT_FEOF() )
  967.  *  $SEEALSO$
  968.  *     FT_FREADLN() FT_FRECNO() FT_FINSERT() FT_FDELETE()
  969.  *  $END$
  970.  */
  971.  
  972. void pascal ft_fwritel( )
  973. {
  974.    int x;
  975.    char *buf = _parc( 1 );
  976.    int buf_len = _parclen( 1 );
  977.    long read1;
  978.    long read2;
  979.    long end1;
  980.    long end2;
  981.  
  982.    int b_ptr   = _vAlloc( b_size, 0 );
  983.    int c_ptr   = _vAlloc( c_size, 0 );
  984.    int c2_ptr  = _vAlloc( c_size, 0 );
  985.    char  *b    = _vLock( b_ptr);
  986.    char  *c    = _vLock( c_ptr);
  987.    char  *c2   = _vLock( c2_ptr);
  988.  
  989. /* find end of first record to be replaced */
  990.  
  991.    end1  = _tlseek( handles[area], offset[area], SEEK_SET );
  992.    read1 = _tread ( handles[area], b, b_size );
  993.  
  994. /* if insert mode, leave pointer alone and skip below */
  995.  
  996.    if ( ISNUM(2) && _parl( 2 ) ) {
  997.       x = 0;
  998.    }
  999.    else {
  1000.  
  1001.       x = _findeol( b, (int)read1 );
  1002.    }
  1003.  
  1004. /* save two buffers' worth of data from end of record on */
  1005.  
  1006.    _tlseek( handles[area], (int)end1 + x, SEEK_SET );
  1007.    read1 = _tread( handles[area], c, c_size );   /* now read in a big glob */
  1008.    read2 = _tread( handles[area], c2, c_size );   /* now read in a big glob */
  1009.    end2  = end1 + read1 + read1 + x;
  1010.  
  1011. /* write the new record */
  1012.  
  1013.    _tlseek( handles[area], (int)end1, SEEK_SET );
  1014.    _twrite( handles[area], buf, buf_len );
  1015.    end1 += (long)buf_len;
  1016.  
  1017.    end1 = _filewrite( read1, read2, end1, end2, c, c2 ); /* loop to write */
  1018.  
  1019.    _tlseek( handles[area], (int)end1, SEEK_SET );
  1020.    _twrite( handles[area], c, 0 );
  1021.    last_rec[area] = 0;
  1022.    lastbyte[area] = _tlseek( handles[area], 0L, SEEK_END );
  1023.    _vUnLock( b_ptr );
  1024.    _vUnLock( c_ptr );
  1025.    _vUnLock( c2_ptr);
  1026.    _vFree( b_ptr );
  1027.    _vFree( c_ptr );
  1028.    _vFree( c2_ptr);
  1029. }
  1030.  
  1031. /*  $DOC$
  1032.  *  $FUNCNAME$
  1033.  *     FT_FLASTRE()
  1034.  *  $CATEGORY$
  1035.  *     File I/O
  1036.  *  $ONELINER$
  1037.  *     Get the no. of records in the currently selected text file
  1038.  *  $SYNTAX$
  1039.  *     FT_FLASTRE() -> nLastRecordNum
  1040.  *  $ARGUMENTS$
  1041.  *     None
  1042.  *  $RETURNS$
  1043.  *     An integer containing the number of records in the text file in
  1044.  *     the currently selected text file workarea, or zero if no file
  1045.  *     is currently open in the workarea.
  1046.  *  $DESCRIPTION$
  1047.  *     This function returns the number of the last record in a text file.
  1048.  *
  1049.  *     A text file "record" is a line of text terminated by a CRLF pair.
  1050.  *  $EXAMPLES$
  1051.  *     FT_FUSE( "text.c" )
  1052.  *     ? FT_FLASTRE()
  1053.  *  $SEEALSO$
  1054.  *     FT_FUSE() FT_FRECNO()
  1055.  *  $END$
  1056.  */
  1057.  
  1058. void pascal ft_flastre( )
  1059. {
  1060.  
  1061.    long old_rec;
  1062.    long old_offset;
  1063.  
  1064.    old_rec = recno[area];
  1065.    old_offset = offset[area];
  1066.  
  1067.    ft_fgobot();
  1068.    _retnl( last_rec[area] );
  1069.  
  1070.    recno[area] = old_rec;
  1071.    offset[area] = old_offset;
  1072.  
  1073. }
  1074.  
  1075. /*  $DOC$
  1076.  *  $FUNCNAME$
  1077.  *     FT_FEOF()
  1078.  *  $CATEGORY$
  1079.  *     File I/O
  1080.  *  $ONELINER$
  1081.  *     Determine when end of text file is encountered
  1082.  *  $SYNTAX$
  1083.  *     FT_FEOF() -> lResult
  1084.  *  $ARGUMENTS$
  1085.  *     None
  1086.  *  $RETURNS$
  1087.  *     .T. if an attempt was made to skip past the last record of
  1088.  *     the currently selected text file, otherwise .F.
  1089.  *  $DESCRIPTION$
  1090.  *     This function is similar to the CLIPPER Eof() function.
  1091.  *
  1092.  *     A text file "record" is a line of text terminated by a CRLF pair.
  1093.  *  $EXAMPLES$
  1094.  *     FT_FUSE( "FTTEXT.C" )
  1095.  *     
  1096.  *     ? FT_FEOF()        // .F.
  1097.  *     FT_FSKIP()
  1098.  *     ? FT_FEOF()        // .T.
  1099.  *  $SEEALSO$
  1100.  *     FT_FUSE() FT_FSKIP()
  1101.  *  $END$
  1102.  */
  1103.  
  1104.  
  1105. void pascal ft_feof()
  1106. {
  1107.    _retl( isEof[area] );
  1108. }
  1109.  
  1110.  
  1111. /*  $DOC$
  1112.  *  $FUNCNAME$
  1113.  *     FT_FGOTO()
  1114.  *  $CATEGORY$
  1115.  *     File I/O
  1116.  *  $ONELINER$
  1117.  *     Move record pointer to specific record in a text file
  1118.  *  $SYNTAX$
  1119.  *     FT_FGOTO( nLine ) -> NIL
  1120.  *  $ARGUMENTS$
  1121.  *     <nLine> is the record number to go to.
  1122.  *  $RETURNS$
  1123.  *     NIL
  1124.  *  $DESCRIPTION$
  1125.  *     This function moves the record pointer to a specific record
  1126.  *     in the file in the currently selected text file workarea.  If
  1127.  *     the record number requested is greater than the number of records
  1128.  *     in the file, the record pointer will be positioned at the last
  1129.  *     record.
  1130.  *
  1131.  *     A text file "record" is a line of text terminated by a CRLF pair.
  1132.  *  $EXAMPLES$
  1133.  *     // read 5th line of text from file
  1134.  *     FT_FUSE( "FTTEXT.C" )
  1135.  *     FT_FGOTO(5)
  1136.  *     cText := FT_FREADLN()
  1137.  *  $SEEALSO$
  1138.  *    FT_FRECNO() FT_FGOTOP() FT_FREADLN()
  1139.  *  $END$
  1140.  */
  1141.  
  1142. void pascal ft_fgoto()
  1143. {
  1144.  
  1145.    long target;
  1146.    long last;
  1147.  
  1148.    target = _parnl(1);
  1149.    last = 0;
  1150.  
  1151.    if ( recno[area] > target ) {
  1152.       while ( recno[area] != target )   {
  1153.          last = recno[area];
  1154.          _ft_skip(-1);
  1155.          if ( recno[area] == last )
  1156.             break;
  1157.       }
  1158.    }
  1159.    else {
  1160.       while ( recno[area] != target ) {
  1161.          last = recno[area];
  1162.          _ft_skip(1);
  1163.          if ( recno[area] == last )
  1164.             break;
  1165.       }
  1166.    }
  1167. }
  1168.  
  1169. /*----------------------------------------------------------------------
  1170.  
  1171.    _findeol()  -  In-line assembler routine to parse a buffer
  1172.                   for a CRLF pair
  1173.  
  1174. ------------------------------------------------------------------------*/
  1175. static int _findeol( char *buf, int buf_len )
  1176. {
  1177.    _asm                    /* for TASM omit leading underscore */
  1178.    {
  1179.       push  di             ; save flags and registers
  1180.             push    es
  1181.       pushf
  1182.       cld                  ; move forward
  1183.       les   di, buf        ; point to buffer
  1184.       mov   bx, di         ; save buffer start for offset calc later
  1185.       mov   cx, buf_len    ; scan entire buffer
  1186.       mov   al, 13
  1187. _feol1:repne  scasb        ; look for a CR
  1188.       jcxz  _feolerr       ; no find, return entire buffer
  1189.       cmp   es:[di], 10    ; got a CRLF pair?
  1190.       jne   _feol2
  1191.       dec   di             ; yes, point to CR and return
  1192.       jmp   _feoldone
  1193. _feol2:cmp  es:[di-2], 10  ; nope, check for LFCR pair
  1194.       jne   _feol1
  1195.       dec   di             ; yes, point to LF & return
  1196.       dec   di
  1197.       jmp   _feoldone      ; otherwise keep looking
  1198.  
  1199. _feolerr:
  1200.       mov   di, bx         ; on no find return entire length of buffer
  1201.       add   di, buf_len    ;  but truncate any LF or EOF markers
  1202.             cmp        byte ptr es:[di-1], 1Ah ; test for end of file marker
  1203.             je        _feolerr1
  1204.             cmp        byte ptr es:[di-1], 0Ah ; test for an errant LF
  1205.             jne        _feoldone
  1206. _feolerr1:
  1207.             dec        di
  1208. _feoldone:
  1209.       mov   ax, di         ; subtract current pointer pos from start to
  1210.       sub   ax, bx         ;  learn offset within buffer
  1211.       popf
  1212.             pop        es
  1213.       pop   di
  1214.    }
  1215. }     /* end _findeol() */
  1216.  
  1217.  
  1218. /*----------------------------------------------------------------------
  1219.  
  1220.    _findbol()  -  In-line assembler routine to parse a buffer
  1221.                   for a CRLF pair
  1222.  
  1223. ------------------------------------------------------------------------*/
  1224. static int _findbol( char *buf, int buf_len )
  1225. {
  1226.    _asm                    /* for TurboC, use "asm" */
  1227.    {
  1228.       push  di             ; save flags and registers
  1229.             push    es
  1230.       pushf
  1231.       std                  ; move back'rdz
  1232.       les   di, buf        ; point to buffer tail
  1233.       mov   bx, di         ; save buffer start for offset calc later
  1234.       add   di, buf_len
  1235.       mov   cx, buf_len    ; scan entire buffer
  1236.       mov   al, 13
  1237. _fbol1:repne  scasb        ; look for a CR
  1238.       jcxz  _fbolerr       ; no find, return entire buffer
  1239.       cmp   es:[di], 10    ; got a LFCR pair?
  1240.       je   _fboldone       ; yup, round'm up and head'm home, boys
  1241.       cmp  es:[di+2], 10  ; check for CRLF pair
  1242.       jne   _fbol1
  1243.       inc   di             ; yes, point to CR
  1244.       jmp   _fboldone      ; otherwise keep looking
  1245.  
  1246. _fbolerr:
  1247.       mov   di, bx         ; on no find return length of buffer
  1248. _fboldone:
  1249.       mov   ax, di         ; subtract current pointer pos from start to
  1250.       sub   ax, bx         ;  learn offset within buffer
  1251.       popf
  1252.             pop    es
  1253.       pop   di
  1254.    }
  1255. }     /* end _findbol() */
  1256.  
  1257. /*----------------------------------------------------------------------
  1258.  
  1259.    _filewrite()-  moves text up or down in a file after an insert or delete
  1260.  
  1261. ------------------------------------------------------------------------*/
  1262. static long _filewrite( long read1, long read2, long end1, long end2,
  1263.                         char *c, char *c2 )
  1264. {
  1265.    do {
  1266.       _tlseek( handles[area], (int)end1, SEEK_SET );
  1267.       _twrite( handles[area], c, (int)read1 );
  1268.       end1 += read1 ;     /* end1 should now point to eof */
  1269.  
  1270.       if ( read2 == 0 ) {
  1271.          break;
  1272.       }
  1273.  
  1274.       (int)_tlseek( handles[area], (int)end2, SEEK_SET );
  1275.       read1 = _tread( handles[area], c, c_size );
  1276.       end2 += read1;
  1277.  
  1278.       _tlseek( handles[area], (int)end1, SEEK_SET );
  1279.       _twrite( handles[area], c2, (int)read2 );
  1280.       end1 += read2 ;
  1281.  
  1282.       (int)_tlseek( handles[area], (int)end2, SEEK_SET );
  1283.       read2 = _tread( handles[area], c2, c_size );   /* now read in a big glob */
  1284.       end2 += read2 ;
  1285.  
  1286.    } while ( read1 > 0 );
  1287.  
  1288.    return ( end1 );
  1289.  
  1290. }     /* end _filewrite() */
  1291.  
  1292. /*  fttext.c  eof */
  1293.