home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Media Share 9
/
MEDIASHARE_09.ISO
/
progmisc
/
nfsrc21.zip
/
FTTEXT.C
< prev
next >
Wrap
C/C++ Source or Header
|
1992-10-17
|
38KB
|
1,293 lines
/*
* File......: TEXT.C
* Author....: Brice de Ganahl and Steve Larsen
* CIS ID....: 76370,1532
* Date......: $Date: 17 Oct 1992 16:25:16 $
* Revision..: $Revision: 1.7 $
* Log file..: $Logfile: C:/nanfor/src/fttext.c_v $
*
* This is an original work by Brice de Ganahl and Steve Larsen
* and is placed in the public domain.
*
* Doc headers by Glenn Scott, Don Caton, and Steve Larsen
*
* Extensively revised by Steve Larsen
*
* Modification history:
* ---------------------
*
* $Log: C:/nanfor/src/fttext.c_v $
*
* Rev 1.7 17 Oct 1992 16:25:16 GLENN
* Leo cleaned up the documentation, including an errant SEEALSO
* reference.
*
* Rev 1.6 03 Oct 1992 02:07:38 GLENN
* Minor adjustments to file header block.
*
* Rev 1.5 03 Oct 1992 02:03:44 GLENN
* Major modifications by Steve Larsen, as follows:
*
* Brice laid some wonderful groundwork with his initial release of
* these functions, however I needed more capability. With his per-
* mission, I have made the following additions/changes to Rev. 1.4:
*
* - Eliminated the problem of memory for buffers being re-allocated every
* time a file got used.
* - Further reduced memory impact by converting from extend system memory
* allocation techniques to virtual memory. To accomplish this, we
* use the Clipper v5.01 r1.29 variants of the "_v" undocumented
* internal functions. If these functions change in future releases, you
* will need to locate them herein and make the appropriate changes.
*
* NOTE: these functions allocate and deallocate virtual memory on an
* "as-needed" basis. If your application makes heavy and frequent use
* of those functions that perform a lot of buffering (ft_fInsert(),
* ft_fDelete() and ft_fWrite()), you might consider modifying the memory
* management scheme used herein, that is, allocate the required buffers
* only once upon the first call to these functions, then recycle them.
* - Added the ability to specify file open mode.
* - Added a function to write to a record, which through a switch can either
* over-write the current record, or insert a new one.
* - Added functions to insert, delete and append a specified number of lines.
* - Fixed the existing functions so that they properly handle "trailers",
* that is, a case where the last chars in a file are not CRLF delimited.
* - Provided checking for the possibility that the file might be terminated
* with ^Z (1Ah), if so, ignoring it (providing consistency with non-^Z
* terminated files). This only occurs on the last record of a file.
* - Eliminated a potential problem if one were to issue an ft_fUse() prior
* actually opening any files.
* - Replaced the original C parsing logic to determine the end-of-line (CRLF)
* with an optimized assembler routine. This bypassed a significant
* performance hit.
* - The original header (FTTEXT.h) file in now incorporated in this one file.
* This is not necessarily an enhancement, more like laziness.
* - Provided the (followup) author with his very first C experience!
*
* Steve Larsen, Dec. 7, 1991 CIS 76370,1532
*
* - Function changes/additions (refer to the individual doc headers for
* details):
*
* FT_FSELECT( [ < nArea > ] ) -> nArea
* FT_FUSE( [ < cFile > ][, < nMode > ] ) -> nHandle | NIL
* FT_FWRITELN( < cData > [, < lInsert > ] ) -> NIL
* FT_FINSERT( [ < nLines > ] ) -> NIL
* FT_FDELETE( [ < nLines > ] ) -> NIL
* FT_FAPPEND( [ < nLines > ] ) -> NIL
*
* Internal Steve Larsen revisions:
*
* 12/07/91 Original rework
* 02/13/92 Fixed _findeol(), FT_FREADLN() and FT_FGOBOT() to
* better handle files with CRLF, LF, ^Z or nothing
* at the EOF. Previously, under some conditions the
* last record was chopped by a character, depending
* on the last character(s).
* 05/02/92 Fixed buffering and VMM allocation problem with
* FT_FGOBOT().
* 08/26/92 Correcting problem when appending blank lines to an
* empty file (ft_fAppend() and ft_fWriteLn()).
*
*
* Rev 1.4 17 Aug 1991 15:31:08 GLENN
* Don Caton fixed some spelling errors in the doc
*
* Rev 1.3 15 Aug 1991 23:08:36 GLENN
* Forest Belt proofread/edited/cleaned up doc
*
* Rev 1.2 29 Apr 1991 08:02:12 GLENN
* Minor adjustments to documentation block
*
* Rev 1.1 29 Apr 1991 08:00:26 GLENN
* ft_flastrec() -- name was longer than 10 characters so linkers couldn't
* find the symbol. Just hacked off the last "c" so it is really
* ft_flastre(). Sorry, folks. -- Glenn
*
* Rev 1.0 01 Apr 1991 01:02:48 GLENN
* Nanforum Toolkit
*
*/
/* Notes:
The Clipper internal functions used seem to be stable across
versions but nothing is guaranteed. These functions begin
with _t, are used for file I/O, and are compatible with their
ANSI counterparts (just strip the _t and you have the ANSI name).
See text.h for the prototypes.
This revision utilizes the in-line assembler feature found in MSC
6.0. If compiling with TurboC substitute "_asm" with "asm".
I compile these functions with the following MicroSoft C parameters:
cl /c /AL /Od /Zl /Zi /FPa /Gs /W3 text.c
Note that the /Od defeats optimization and is necessary only for
compatibility with Blinker, Warplink, etc. If you are not overlaying
this code you may want to change this to /Oalt. Likewise, the
/Zi is for symbolic debugging info which you will want to omit in
any final compiles.
Some sample Clipper code which would use these functions is listed
below. It will print out the contents of this file.
ft_fuse( "text.c" )
do while !ft_feof()
? ft_freadln()
ft_fskip()
enddo
ft_fuse()
*/
/* up this number if you need more than 10 text file areas */
#define TEXT_WORKAREAS 10
#include "extend.h"
#include "stdio.h"
#include "share.h"
#include "fcntl.h"
#define b_size 1024
#define c_size 4096
#ifndef SIZE_T
#define SIZE_T
typedef unsigned int size_t;
#endif
void pascal ft_fseek( void );
void pascal ft_fuse( void );
void pascal ft_fselect( void );
void pascal ft_fgotop( void );
void pascal ft_frecno( void );
void pascal ft_fgobot( void );
void pascal ft_fskip( void );
void pascal ft_freadln( void );
void pascal ft_flastre( void );
void pascal ft_feof( void );
void pascal ft_fgoto( void );
void pascal ft_fwritel( void );
void pascal ft_fdelete( void );
void pascal ft_fappend( void );
int _findeol( char *buf, int buf_len ); /* in-line ASM */
int _findbol( char *buf, int buf_len ); /* in-line ASM */
/*void _ftwrite( char *buf, int insert ); (replaced below) */
long _filewrite( long read1, long read2, long end1, long end2,
char *c, char *c2 );
long _ft_skip( int recs );
extern int _tclose( int );
extern int _tcreat( char*, int );
extern int _terror;
extern long _tlseek( int, long, int );
extern int _topen( char*, int );
extern int _tread( int, char*, int );
extern int _twrite( int, char*, int );
extern int _tcommit( int );
/* extern int strlen( char* ); */
extern int _vAlloc( int, int );
extern char *_vLock( int );
extern void _vUnLock( int );
extern void _vFree( int );
static long recno[TEXT_WORKAREAS];
static long offset[TEXT_WORKAREAS];
static int handles[TEXT_WORKAREAS];
static int area = 0;
static long last_rec[TEXT_WORKAREAS];
static long last_off[TEXT_WORKAREAS];
static long lastbyte[TEXT_WORKAREAS];
static int isEof[TEXT_WORKAREAS];
/* $DOC$
* $FUNCNAME$
* FT_FUSE()
* $CATEGORY$
* File I/O
* $ONELINER$
* Open or close a text file for use by the FT_F* functions
* $SYNTAX$
* FT_FUSE( [ <cFile> ] [, <nMode> ] ) -> nHandle | NIL
* $ARGUMENTS$
* <cFile> is the text file you want to open. If not specified,
* the file currently open, if any, will be closed.
*
* <nMode> is the open mode for the file. Please refer to the
* discussion of open modes under FOPEN() in the Clipper manual
* and FILEIO.CH for a list of allowable open modes. If not
* specified, the file will be opened with a mode of
* FO_READ + FO_SHARED (64).
*
* $RETURNS$
* If <cFile> is passed and the file is opened successfully, an
* integer containing the file handle. If the file cannot be
* opened, -1 will be returned.
*
* If FT_FUSE() is called without any arguments, it will close the
* text file in the current "text area" and return NIL.
* $DESCRIPTION$
* The FT_F*() file functions are for reading text files, that is,
* files where each line (record) is delimited by a CRLF pair.
*
* Each file is opened in its own "workarea", similar to the concept
* use by dbf files. As provided, a maximum of 10 files (in 10
* workareas) can be opened (assuming there are sufficient file
* handles available). That number may be increased by modifying
* the #define TEXT_WORKAREAS in the C source code and recompiling.
* $EXAMPLES$
* FT_FUSE( "text.c" ) // open text file
* DO WHILE !FT_FEOF()
* ? FT_FREADLN()
* FT_FSKIP()
* ENDDO
* FT_FUSE() // close file
* $SEEALSO$
* FT_FUSE() FT_FSELECT()
* $END$
*/
void pascal ft_fuse()
{
int attr = ISNUM( 2 ) ? _parni(2) : O_RDONLY|SH_DENYNO|O_BINARY ;
if ( ISCHAR(1) ) {
handles[area] = _topen( _parc(1), attr ) ;
offset[area] = 0 ;
recno[area] = 1;
lastbyte[area] = _tlseek( handles[area], 0L, SEEK_END );
_retni( handles[area] );
}
else {
if ( handles[area] != 0 ) {
_tclose( handles[area] );
_retni(1);
recno[area] = 0L;
offset[area] = 0L;
handles[area] = 0;
last_rec[area] = 0L;
last_off[area] = 0L;
lastbyte[area] = 0L;
isEof[area] = 0;
}
}
}
/* $DOC$
* $FUNCNAME$
* FT_FSELECT()
* $CATEGORY$
* File I/O
* $ONELINER$
* Select a text file workarea
* $SYNTAX$
* FT_FSELECT( [ <nArea> ] ) -> nArea
* $ARGUMENTS$
* <nArea> is the text file workarea to select.
* $RETURNS$
* The current selected text file area.
*
* $DESCRIPTION$
* This function selects a text file "workarea" from 1 to 10. A
* file may or may not be open in the selected area.
*
* Passing 0 for <nArea> selects the next available workarea, similar
* to Clipper's SELECT 0 command.
*
* Each file is opened in its own "workarea", similar to the concept
* used by dbf files. As provided, a maximum of 10 files (in 10
* workareas) can be opened (assuming there are sufficient file
* handles available). That number may be increased by modifying
* the #define TEXT_WORKAREAS in the C source code and recompiling.
*
* All the FT_F*() file functions operate on the file in the currently
* selected text file workarea.
*
* Text file workareas are separate from and independent of Clipper's
* database workareas.
* $EXAMPLES$
* FT_FSELECT(1)
* nFile1 := FT_FUSE( "temp.c" )
* ? FT_FLASTRE() // no. of lines in temp.c
* FT_FSELECT(2)
* nFile2 := FT_FUSE( "temp.h" )
* ? FT_FLASTRE() // no. of lines in temp.h
* $SEEALSO$
* FT_FUSE()
* $END$
*/
void pascal ft_fselect()
{
if ( ISNUM(1) ) {
area = _parni(1) - 1;
if ( area == -1 ) {
for ( area = 0; area < TEXT_WORKAREAS - 1; area++ ) {
if ( handles[area] == 0 ) {
break;
}
}
}
}
_retni( area + 1 );
}
/* $DOC$
* $FUNCNAME$
* FT_FGOTOP()
* $CATEGORY$
* File I/O
* $ONELINER$
* Go to the first record in a text file
* $SYNTAX$
* FT_FGOTOP() -> NIL
* $ARGUMENTS$
* None
* $RETURNS$
* NIL
* $DESCRIPTION$
* This function moves the record pointer to the first record
* in the currently selected text file workarea.
*
* A text file "record" is a line of text terminated by a CRLF pair.
* $EXAMPLES$
* FT_FUSE( "text.c" ) // open text file
* DO WHILE !FT_FEOF()
* ? FT_FREADLN() // read thru file
* FT_FSKIP()
* ENDDO
* FT_FGOTOP() // go back to top
* ? FT_FRECNO() // 1
* $SEEALSO$
* FT_FSELECT() FT_FUSE() FT_FRECNO() FT_FGOBOT()
* $END$
*/
void pascal ft_fgotop()
{
offset[area] = 0L;
recno[area] = 1L;
}
/* $DOC$
* $FUNCNAME$
* FT_FRECNO()
* $CATEGORY$
* File I/O
* $ONELINER$
* Return the current record number of a text file
* $SYNTAX$
* FT_FRECNO() -> nRecNo
* $ARGUMENTS$
* None
* $RETURNS$
* The current record number of a text file or 0 if no file is open.
* $DESCRIPTION$
* This function returns the current record number of the file open
* in the currently selected text file workarea.
*
* A text file "record" is a line of text terminated by a CRLF pair.
* $EXAMPLES$
* FT_FUSE( "text.c" ) // open text file
* DO WHILE !FT_FEOF()
* ? FT_FREADLN() // read thru file
* FT_FSKIP()
* ENDDO
* FT_FGOTOP() // go back to top
* ? FT_FRECNO() // 1
* $SEEALSO$
* FT_FSELECT() FT_FUSE() FT_FGOTOP() FT_FGOBOT()
* $END$
*/
void pascal ft_frecno()
{
_retnl( recno[area] );
}
/* $DOC$
* $FUNCNAME$
* FT_FGOBOT()
* $CATEGORY$
* File I/O
* $ONELINER$
* Go to the last record in a text file
* $SYNTAX$
* FT_FGOBOT() -> NIL
* $ARGUMENTS$
* None
* $RETURNS$
* NIL
* $DESCRIPTION$
* This function moves the record pointer to the last record of the
* file in the currently selected text file workarea.
*
* A text file "record" is a line of text terminated by a CRLF pair.
* $EXAMPLES$
* // read last line
* FT_FUSE( "text.c" )
* FT_FGOBOT()
* ? FT_FREADLN()
* $SEEALSO$
* FT_FSELECT() FT_FUSE() FT_FGOTOP() FT_FRECNO() FT_FREADLN()
* $END$
*/
void pascal ft_fgobot()
{
int x;
int len, blen;
long loc;
int c_ptr;
char * c, * d;
if ( last_rec[area] != 0 ) {
recno[area] = last_rec[area];
offset[area] = last_off[area];
}
else {
c_ptr = _vAlloc( c_size, 0 );
d = _vLock( c_ptr );
loc = 0L;
do {
c = d;
_tlseek( handles[area], offset[area], SEEK_SET );
len = _tread( handles[area], c, c_size );
blen = len;
loc = offset[area];
do {
x = _findeol( c, len );
if ( ( x == len ) || ( ( x + 1 ) == len ) ) {
break;
}
c += x + 2;
len -= ( x + 2 );
recno[area]++;
loc += x + 2;
} while ( ( len > 0 ) );
offset[area] = loc;
} while ( blen == c_size );
offset[area] = loc;
last_rec[area] = recno[area];
last_off[area] = offset[area];
_vUnLock( c_ptr );
_vFree( c_ptr );
}
}
/* $DOC$
* $FUNCNAME$
* FT_FSKIP()
* $CATEGORY$
* File I/O
* $ONELINER$
* Move the record pointer to a new position in a text file
* $SYNTAX$
* FT_FSKIP( [ <nLines> ] ) -> NIL
* $ARGUMENTS$
* <nLines> is the number of lines to skip. Defaults to 1 if
* not specified.
* $RETURNS$
* NIL
* $DESCRIPTION$
* This function moves the text file record pointer, similar to
* the CLIPPER SKIP command.
*
* A text file "record" is a line of text terminated by a CRLF pair.
* $EXAMPLES$
* // display each record of a text file
* FT_FUSE( "text.c" )
* DO WHILE ! FT_FEOF()
* ? FT_FREADLN()
* FT_FSKIP()
* ENDDO
* $SEEALSO$
* FT_FRECNO() FT_FGOTOP()
* $END$
*/
void pascal ft_fskip( void )
{
if ( ISNUM(1) )
_ft_skip( _parni(1) );
else
_ft_skip(1);
}
static long _ft_skip( int recs )
{
int x;
long read_pos;
size_t len;
long y;
int b_ptr = _vAlloc( b_size, 0 );
char *b = _vLock( b_ptr );
if ( recs > 0 ) {
for (y = 0; y < recs; y++ ) {
_tlseek( handles[area], offset[area], SEEK_SET );
len = _tread( handles[area], b, b_size );
x = _findeol( b, len );
if (( x != (int)len ) && ( (offset[area] + x + 2) < lastbyte[area] )) {
isEof[area] = FALSE;
offset[area] += (x + 2);
recno[area]++;
}
else
isEof[area] = TRUE;
}
}
else {
recs = -recs;
isEof[area] = FALSE;
if ( (recno[area] - recs) >= 1 ) // <--- reversed if condition
{ // <--- added opening brace
for (y = recs; y > 0; y-- ) {
if ( offset[area] - b_size < 0L ) {
read_pos = 0;
len = (size_t)offset[area];
}
else {
read_pos = (size_t)(offset[area] - b_size);
len = b_size;
}
_tlseek( handles[area], read_pos, SEEK_SET );
len = _tread( handles[area], b, len );
x = _findbol( b, len-3 ) ;
if ( x < 0 ) {
offset[area] = 0;
recno[area] = 1;
}
else {
offset[area] = read_pos + x + 2;
recno[area]--;
}
}
} // <--- added closing brace
else {
offset[area] = 0;
recno[area] = 1;
}
}
_vUnLock( b_ptr );
_vFree( b_ptr );
return ( recno[area] );
}
/* $DOC$
* $FUNCNAME$
* FT_FREADLN()
* $CATEGORY$
* File I/O
* $ONELINER$
* Read a line from the currently selected text file
* $SYNTAX$
* FT_FREADLN() -> cLine
* $ARGUMENTS$
* None
* $RETURNS$
* A string containing the current record in a text file.
* $DESCRIPTION$
* This function returns a line of text read from the file in the
* currently selected text file workarea. Text lines are delimited
* with a CRLF pair. The record pointer is not moved.
*
* A text file "record" is a line of text terminated by a CRLF pair.
* $EXAMPLES$
* // display each record of a text file
* FT_FUSE( "text.c" )
* DO WHILE ! FT_FEOF()
* ? FT_FREADLN()
* FT_FSKIP()
* ENDDO
* $SEEALSO$
* FT_FUSE() FT_FWRITELN() FT_FRECNO() FT_FGOTOP()
* $END$
*/
void pascal ft_freadln()
{
int x;
int read;
int b_ptr = _vAlloc( b_size, 0 );
char *b = _vLock( b_ptr );
_tlseek( handles[area], offset[area], SEEK_SET );
read = (int) _tread( handles[area], b, b_size );
x = _findeol( b, read );
_retclen( b, x );
_vUnLock( b_ptr );
_vFree( b_ptr );
}
/* $DOC$
* $FUNCNAME$
* FT_FDELETE()
* $CATEGORY$
* File I/O
* $ONELINER$
* Deletes a line from the currently selected text file
* $SYNTAX$
* FT_FDELETE( [ < nLines > ] ) -> NIL
* $ARGUMENTS$
* <nLines> is the number of lines to be eliminated, beginning with
* the current record position.
*
* If <nLines> is omitted, the current record is deleted only.
*
* $RETURNS$
* NIL
* $DESCRIPTION$
* This function deletes one or several lines of text from the file
* in the currently selected text file workarea. Text lines are
* delimited with a CRLF pair. The record pointer is not moved.
* $EXAMPLES$
* // delete the next 4 lines from a file
* FT_FUSE( "test.txt" )
*
* FT_FDELETE( 4 )
* $SEEALSO$
* FT_FAPPEND() FT_FRECNO() FT_FINSERT()
* $END$
*/
void pascal ft_fdelete( )
{
int no_lines = ( ISNUM( 1 ) ? _parni( 1 ) : 1 );
long read1;
long read2;
long end1;
long end2;
long cur_rec = recno[area];
long cur_off = offset[area];
int b_ptr = _vAlloc( b_size, 0 );
int c_ptr = _vAlloc( c_size, 0 );
int c2_ptr = _vAlloc( c_size, 0 );
char *b = _vLock( b_ptr);
char *c = _vLock( c_ptr);
char *c2 = _vLock( c2_ptr);
/* save address to current record ( first record to be deleted ) */
end1 = offset[area] ;
/* skip over deleted records, point to first 'to be retained' record */
_ft_skip( no_lines ) ;
_tlseek( handles[area], offset[area], SEEK_SET );
/* save two buffers' worth of data */
read1 = _tread( handles[area], c, c_size ); /* now read in a big glob */
read2 = _tread( handles[area], c2, c_size ); /* now read in a big glob */
end2 = offset[area] + read1 + read1;
end1 = _filewrite( read1, read2, end1, end2, c, c2 ); /* loop to write */
_tlseek( handles[area], (int)end1, SEEK_SET );
_twrite( handles[area], c, 0 );
last_rec[area] = 0;
lastbyte[area] = _tlseek( handles[area], 0L, SEEK_END );
recno[area] = cur_rec;
offset[area]= cur_off;
_vUnLock( b_ptr );
_vUnLock( c_ptr );
_vUnLock( c2_ptr);
_vFree( b_ptr );
_vFree( c_ptr );
_vFree( c2_ptr);
}
/* $DOC$
* $FUNCNAME$
* FT_FINSERT()
* $CATEGORY$
* File I/O
* $ONELINER$
* Inserts a line in the currently selected text file
* $SYNTAX$
* FT_FINSERT( [ < nLines > ] ) -> NIL
* $ARGUMENTS$
* <nLines> is the number of lines that should be inserted at the
* current record position.
*
* If <nLines> is omitted, one record is inserted.
*
* $RETURNS$
* NIL
* $DESCRIPTION$
* This function inserts a line of text in the file in the currently
* selected text file workarea. Text lines are delimited with a
* CRLF pair. The record pointer is not moved.
*
* An optional parameter allows multiple insertions to take place
* with a single call to FT_FINSERT().
*
* A text file "record" is a line of text terminated by a CRLF pair.
* Each line inserted with this function will be empty.
* $EXAMPLES$
* // add a blank line of text to a file
* FT_FUSE( "test.txt" )
*
* FT_FINSERT()
* $SEEALSO$
* FT_FAPPEND() FT_FRECNO() FT_FDELETE() FT_FLASTRE()
* $END$
*/
void pascal ft_finsert( )
{
int no_lines = ( ISNUM( 1 ) ? _parni( 1 ) : 1 );
long read1;
long read2;
long end1;
long end2;
char crlf[] = { (char)0x0D, (char)0x0A };
int b_ptr = _vAlloc( b_size, 0 );
int c_ptr = _vAlloc( c_size, 0 );
int c2_ptr = _vAlloc( c_size, 0 );
char *b = _vLock( b_ptr);
char *c = _vLock( c_ptr);
char *c2 = _vLock( c2_ptr);
/* find end of first record to be replaced */
end1 = _tlseek( handles[area], offset[area], SEEK_SET );
/* save two buffers' worth of data from current record */
read1 = _tread( handles[area], c, c_size ); /* now read in a big glob */
read2 = _tread( handles[area], c2, c_size ); /* now read in a big glob */
end2 = end1 + read1 + read2;
/* write the new records */
_tlseek( handles[area], (int)end1, SEEK_SET );
/* need this loop to consider that No. lines inserted may be more than
the amount saved by the two buffers above */
do {
_twrite( handles[area], crlf, 2 );
end1 += 2;
} while ( --no_lines );
end1 = _filewrite( read1, read2, end1, end2, c, c2 ); /* loop to write */
lastbyte[area] = _tlseek( handles[area], (int)end1, SEEK_SET );
last_rec[area] = 0L;
_twrite( handles[area], c, 0 );
_vUnLock( b_ptr );
_vUnLock( c_ptr );
_vUnLock( c2_ptr);
_vFree( b_ptr );
_vFree( c_ptr );
_vFree( c2_ptr);
}
/* $DOC$
* $FUNCNAME$
* FT_FAPPEND()
* $CATEGORY$
* File I/O
* $ONELINER$
* Appends a line to the currently selected text file
* $SYNTAX$
* FT_FAPPEND( [ < nLines > ] ) -> NIL
* $ARGUMENTS$
* <nLines> is the number of lines that should be appended to the
* end of the currently selected text file.
*
* If <nLines> is omitted, one record is appended.
*
* $RETURNS$
* NIL
* $DESCRIPTION$
* This function appends a line of text to the file in the currently
* selected text file workarea. Text lines are delimited with a
* CRLF pair. The record pointer is moved to the last appended
* record.
*
* Multiple lines may be appended with one call to FT_FAPPEND().
*
* A text file "record" is a line of text terminated by a CRLF pair.
* Each line appended with this function will be empty.
*
* NOTE: Occasionally a text file may contain a non-CRLF terminated
* line, at the end of the file ("stragglers"). This function assumes
* these stragglers to be the last line of the file, and begins
* appending the new lines after this line. In other words, if the
* last line in the text file is not terminated with a CRLF pair prior
* to calling FT_FAPPEND(), the function will terminate that last line
* before appending any new lines.
*
* $EXAMPLES$
* // add a blank line of text to a file
* FT_FUSE( "test.txt" )
*
* ?FT_FRECNO() // displays 5
*
* FT_FAPPEND()
*
* ?FT_FRECNO() // displays 6
* $SEEALSO$
* FT_FRECNO() FT_FDELETE() FT_FINSERT() FT_FLASTRE()
* $END$
*/
void pascal ft_fappend( )
{
int no_lines = ( ISNUM( 1 ) ? _parni( 1 ) : 1 );
long read1;
long end1;
int x;
char crlf[] = { (char)0x0D, (char)0x0A };
int b_ptr = _vAlloc( b_size, 0 );
char *b = _vLock( b_ptr);
/* go to end of file */
ft_fgobot();
/* find end of record */
end1 = _tlseek( handles[area], offset[area], SEEK_SET );
read1 = _tread( handles[area], b, b_size ); /* now read in a big glob */
/* determine if CRLF pair exists, if not, add one */
if ( _findeol( b, (int)read1 ) == (int)read1 ) {
_tlseek( handles[area], lastbyte[area], SEEK_SET );
_twrite( handles[area], crlf, 2 );
no_lines--;
}
/* loop to write new lines */
for ( x = 0; x < no_lines; x ++ ) {
_twrite( handles[area], crlf, 2 );
}
_vUnLock( b_ptr );
_vFree( b_ptr );
last_rec[area] = 0L;
ft_fgobot();
}
/* $DOC$
* $FUNCNAME$
* FT_FWRITELN()
* $CATEGORY$
* File I/O
* $ONELINER$
* Write a line to the currently selected text file
* $SYNTAX$
* FT_FWRITELN( < cData >, [ < lInsert > ] ) -> NIL
* $ARGUMENTS$
* <cData> is a string of data to write to the file at the current
* record position.
*
* <lInsert> is a logical indicating whether the contents
* of the current record are to be preserved, that is, if lInsert
* evaluates to .T., the a new record is inserted at the current
* position. The current record then is pushed down to FT_FRECNO()+1.
*
* If lInsert is .F. or omitted, the current record is replaced by
* cData.
*
* $RETURNS$
* NIL
* $DESCRIPTION$
* This function writes a line of text to the file in the currently
* selected text file workarea. Text lines are delimited with a
* CRLF pair. The record pointer is not moved.
*
* The contents of the current record are updated to reflect the new
* new line written, unless the Insert option is selected.
*
* Writing a null string has the effect of clearing the current line
* if in overstrike mode, else inserting a new line (same as
* FT_FINSERT()).
*
* A text file "record" is a line of text terminated by a CRLF pair.
* $EXAMPLES$
* // write a line of text to a file
* FT_FUSE( "config.sys" )
* DO WHILE UPPER( FT_FREADLN() ) != "FILES=" .AND. !F_FEOF()
* FT_FSKIP()
* ENDDO
*
* FT_FWRITELN( "FILES=30", FT_FEOF() )
* $SEEALSO$
* FT_FREADLN() FT_FRECNO() FT_FINSERT() FT_FDELETE()
* $END$
*/
void pascal ft_fwritel( )
{
int x;
char *buf = _parc( 1 );
int buf_len = _parclen( 1 );
long read1;
long read2;
long end1;
long end2;
int b_ptr = _vAlloc( b_size, 0 );
int c_ptr = _vAlloc( c_size, 0 );
int c2_ptr = _vAlloc( c_size, 0 );
char *b = _vLock( b_ptr);
char *c = _vLock( c_ptr);
char *c2 = _vLock( c2_ptr);
/* find end of first record to be replaced */
end1 = _tlseek( handles[area], offset[area], SEEK_SET );
read1 = _tread ( handles[area], b, b_size );
/* if insert mode, leave pointer alone and skip below */
if ( ISNUM(2) && _parl( 2 ) ) {
x = 0;
}
else {
x = _findeol( b, (int)read1 );
}
/* save two buffers' worth of data from end of record on */
_tlseek( handles[area], (int)end1 + x, SEEK_SET );
read1 = _tread( handles[area], c, c_size ); /* now read in a big glob */
read2 = _tread( handles[area], c2, c_size ); /* now read in a big glob */
end2 = end1 + read1 + read1 + x;
/* write the new record */
_tlseek( handles[area], (int)end1, SEEK_SET );
_twrite( handles[area], buf, buf_len );
end1 += (long)buf_len;
end1 = _filewrite( read1, read2, end1, end2, c, c2 ); /* loop to write */
_tlseek( handles[area], (int)end1, SEEK_SET );
_twrite( handles[area], c, 0 );
last_rec[area] = 0;
lastbyte[area] = _tlseek( handles[area], 0L, SEEK_END );
_vUnLock( b_ptr );
_vUnLock( c_ptr );
_vUnLock( c2_ptr);
_vFree( b_ptr );
_vFree( c_ptr );
_vFree( c2_ptr);
}
/* $DOC$
* $FUNCNAME$
* FT_FLASTRE()
* $CATEGORY$
* File I/O
* $ONELINER$
* Get the no. of records in the currently selected text file
* $SYNTAX$
* FT_FLASTRE() -> nLastRecordNum
* $ARGUMENTS$
* None
* $RETURNS$
* An integer containing the number of records in the text file in
* the currently selected text file workarea, or zero if no file
* is currently open in the workarea.
* $DESCRIPTION$
* This function returns the number of the last record in a text file.
*
* A text file "record" is a line of text terminated by a CRLF pair.
* $EXAMPLES$
* FT_FUSE( "text.c" )
* ? FT_FLASTRE()
* $SEEALSO$
* FT_FUSE() FT_FRECNO()
* $END$
*/
void pascal ft_flastre( )
{
long old_rec;
long old_offset;
old_rec = recno[area];
old_offset = offset[area];
ft_fgobot();
_retnl( last_rec[area] );
recno[area] = old_rec;
offset[area] = old_offset;
}
/* $DOC$
* $FUNCNAME$
* FT_FEOF()
* $CATEGORY$
* File I/O
* $ONELINER$
* Determine when end of text file is encountered
* $SYNTAX$
* FT_FEOF() -> lResult
* $ARGUMENTS$
* None
* $RETURNS$
* .T. if an attempt was made to skip past the last record of
* the currently selected text file, otherwise .F.
* $DESCRIPTION$
* This function is similar to the CLIPPER Eof() function.
*
* A text file "record" is a line of text terminated by a CRLF pair.
* $EXAMPLES$
* FT_FUSE( "FTTEXT.C" )
*
* ? FT_FEOF() // .F.
* FT_FSKIP()
* ? FT_FEOF() // .T.
* $SEEALSO$
* FT_FUSE() FT_FSKIP()
* $END$
*/
void pascal ft_feof()
{
_retl( isEof[area] );
}
/* $DOC$
* $FUNCNAME$
* FT_FGOTO()
* $CATEGORY$
* File I/O
* $ONELINER$
* Move record pointer to specific record in a text file
* $SYNTAX$
* FT_FGOTO( nLine ) -> NIL
* $ARGUMENTS$
* <nLine> is the record number to go to.
* $RETURNS$
* NIL
* $DESCRIPTION$
* This function moves the record pointer to a specific record
* in the file in the currently selected text file workarea. If
* the record number requested is greater than the number of records
* in the file, the record pointer will be positioned at the last
* record.
*
* A text file "record" is a line of text terminated by a CRLF pair.
* $EXAMPLES$
* // read 5th line of text from file
* FT_FUSE( "FTTEXT.C" )
* FT_FGOTO(5)
* cText := FT_FREADLN()
* $SEEALSO$
* FT_FRECNO() FT_FGOTOP() FT_FREADLN()
* $END$
*/
void pascal ft_fgoto()
{
long target;
long last;
target = _parnl(1);
last = 0;
if ( recno[area] > target ) {
while ( recno[area] != target ) {
last = recno[area];
_ft_skip(-1);
if ( recno[area] == last )
break;
}
}
else {
while ( recno[area] != target ) {
last = recno[area];
_ft_skip(1);
if ( recno[area] == last )
break;
}
}
}
/*----------------------------------------------------------------------
_findeol() - In-line assembler routine to parse a buffer
for a CRLF pair
------------------------------------------------------------------------*/
static int _findeol( char *buf, int buf_len )
{
_asm /* for TASM omit leading underscore */
{
push di ; save flags and registers
push es
pushf
cld ; move forward
les di, buf ; point to buffer
mov bx, di ; save buffer start for offset calc later
mov cx, buf_len ; scan entire buffer
mov al, 13
_feol1:repne scasb ; look for a CR
jcxz _feolerr ; no find, return entire buffer
cmp es:[di], 10 ; got a CRLF pair?
jne _feol2
dec di ; yes, point to CR and return
jmp _feoldone
_feol2:cmp es:[di-2], 10 ; nope, check for LFCR pair
jne _feol1
dec di ; yes, point to LF & return
dec di
jmp _feoldone ; otherwise keep looking
_feolerr:
mov di, bx ; on no find return entire length of buffer
add di, buf_len ; but truncate any LF or EOF markers
cmp byte ptr es:[di-1], 1Ah ; test for end of file marker
je _feolerr1
cmp byte ptr es:[di-1], 0Ah ; test for an errant LF
jne _feoldone
_feolerr1:
dec di
_feoldone:
mov ax, di ; subtract current pointer pos from start to
sub ax, bx ; learn offset within buffer
popf
pop es
pop di
}
} /* end _findeol() */
/*----------------------------------------------------------------------
_findbol() - In-line assembler routine to parse a buffer
for a CRLF pair
------------------------------------------------------------------------*/
static int _findbol( char *buf, int buf_len )
{
_asm /* for TurboC, use "asm" */
{
push di ; save flags and registers
push es
pushf
std ; move back'rdz
les di, buf ; point to buffer tail
mov bx, di ; save buffer start for offset calc later
add di, buf_len
mov cx, buf_len ; scan entire buffer
mov al, 13
_fbol1:repne scasb ; look for a CR
jcxz _fbolerr ; no find, return entire buffer
cmp es:[di], 10 ; got a LFCR pair?
je _fboldone ; yup, round'm up and head'm home, boys
cmp es:[di+2], 10 ; check for CRLF pair
jne _fbol1
inc di ; yes, point to CR
jmp _fboldone ; otherwise keep looking
_fbolerr:
mov di, bx ; on no find return length of buffer
_fboldone:
mov ax, di ; subtract current pointer pos from start to
sub ax, bx ; learn offset within buffer
popf
pop es
pop di
}
} /* end _findbol() */
/*----------------------------------------------------------------------
_filewrite()- moves text up or down in a file after an insert or delete
------------------------------------------------------------------------*/
static long _filewrite( long read1, long read2, long end1, long end2,
char *c, char *c2 )
{
do {
_tlseek( handles[area], (int)end1, SEEK_SET );
_twrite( handles[area], c, (int)read1 );
end1 += read1 ; /* end1 should now point to eof */
if ( read2 == 0 ) {
break;
}
(int)_tlseek( handles[area], (int)end2, SEEK_SET );
read1 = _tread( handles[area], c, c_size );
end2 += read1;
_tlseek( handles[area], (int)end1, SEEK_SET );
_twrite( handles[area], c2, (int)read2 );
end1 += read2 ;
(int)_tlseek( handles[area], (int)end2, SEEK_SET );
read2 = _tread( handles[area], c2, c_size ); /* now read in a big glob */
end2 += read2 ;
} while ( read1 > 0 );
return ( end1 );
} /* end _filewrite() */
/* fttext.c eof */