home *** CD-ROM | disk | FTP | other *** search
/ C!T ROM 2 / ctrom_ii_b.zip / ctrom_ii_b / PROGRAM / CLIPPER / NFPAT4 / FTTEXT.C next >
C/C++ Source or Header  |  1992-12-15  |  38KB  |  1,301 lines

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