719 lines
20 KiB
C
719 lines
20 KiB
C
#include <stdio.h>
|
|
#include <malloc.h>
|
|
#include <string.h>
|
|
#include <memory.h>
|
|
#include <windows.h>
|
|
#include "list.h"
|
|
|
|
// ReaderThread - Reads from the file
|
|
//
|
|
// This thread is woken up by clearing SemReader,
|
|
// then vReaderFlag instructs the thread on the course of
|
|
// action to take. When displaying gets to close to the end
|
|
// of the buffer pool, vReadFlag is set and this thread is
|
|
// started.
|
|
|
|
#if _MSC_FULL_VER >= 13008827
|
|
#pragma warning(push)
|
|
#pragma warning(disable:4715) // Not all control paths return (due to infinite loop)
|
|
#endif
|
|
|
|
DWORD
|
|
ReaderThread (
|
|
DWORD dwParameter
|
|
)
|
|
{
|
|
unsigned rc, code, curPri = 0;
|
|
UNREFERENCED_PARAMETER(dwParameter);
|
|
|
|
for (; ;) {
|
|
|
|
// go into 'boosted' pririoty until we start
|
|
// working on 'non-critical' read ahead. (Ie, far away).
|
|
|
|
if (curPri != vReadPriBoost) {
|
|
SetThreadPriority( GetCurrentThread(),
|
|
vReadPriBoost );
|
|
curPri = vReadPriBoost;
|
|
}
|
|
WaitForSingleObject(vSemReader, WAITFOREVER);
|
|
ResetEvent(vSemReader);
|
|
code = vReaderFlag;
|
|
for (; ;) {
|
|
|
|
// Due to this loop, a new command may have arrived
|
|
// which takes presidence over the automated command
|
|
|
|
rc = WaitForSingleObject (vSemReader, DONTWAIT);
|
|
if (rc == 0) // New command has arrived
|
|
break;
|
|
|
|
switch (code) {
|
|
case F_NEXT: // NEXT FILE
|
|
NewFile ();
|
|
ReadDirect (vDirOffset);
|
|
|
|
// Hack... adjust priority to make first screen look
|
|
// fast. (Ie, reader thread will have lower priority
|
|
// at first; eventhough the display is really close
|
|
// to the end of the buffer)
|
|
|
|
SetThreadPriority( GetCurrentThread(),
|
|
vReadPriNormal );
|
|
|
|
break;
|
|
case F_HOME: // HOME of FILE
|
|
vTopLine = 0L;
|
|
ReadDirect (0L);
|
|
break;
|
|
case F_DIRECT:
|
|
ReadDirect (vDirOffset);
|
|
break;
|
|
case F_DOWN:
|
|
ReadNext ();
|
|
break;
|
|
case F_UP:
|
|
ReadPrev ();
|
|
break;
|
|
case F_END:
|
|
break;
|
|
case F_SYNC:
|
|
ResetEvent(vSemMoreData);
|
|
SetEvent(vSemSync);
|
|
WaitForSingleObject(vSemReader, WAITFOREVER);
|
|
ResetEvent(vSemReader);
|
|
|
|
ResetEvent(vSemSync); // Reset trigger for
|
|
// Next use.
|
|
code = vReaderFlag;
|
|
continue; // Execute Syncronized command
|
|
|
|
case F_CHECK: // No command.
|
|
break;
|
|
default:
|
|
ckdebug (1, "Bad Reader Flag");
|
|
}
|
|
// Command has been processed.
|
|
// Now check to see if read ahead is low, if so set
|
|
// command and loop.
|
|
|
|
if (vpTail->offset - vpCur->offset < vThreshold &&
|
|
vpTail->flag != F_EOF) {
|
|
code = F_DOWN; // Too close to ending
|
|
continue;
|
|
}
|
|
if (vpCur->offset - vpHead->offset < vThreshold &&
|
|
vpHead->offset != vpFlCur->SlimeTOF) {
|
|
code = F_UP; // Too close to begining
|
|
continue;
|
|
}
|
|
|
|
// Not critical, read ahead logic. The current file
|
|
|
|
// Normal priority (below display thread) for this
|
|
if (curPri != vReadPriNormal) {
|
|
SetThreadPriority( GetCurrentThread(),
|
|
vReadPriNormal );
|
|
curPri = vReadPriNormal;
|
|
}
|
|
|
|
if (vCntBlks == vMaxBlks) // All blks in use for
|
|
break; // this one file?
|
|
|
|
if (vpTail->flag != F_EOF) {
|
|
code = F_DOWN;
|
|
continue;
|
|
}
|
|
if (vpHead->offset != vpFlCur->SlimeTOF) {
|
|
code = F_UP;
|
|
continue;
|
|
}
|
|
|
|
|
|
if (vFhandle != 0) { // Must have whole file read in
|
|
CloseHandle (vFhandle); // Close the file, and set flag
|
|
vFhandle = 0;
|
|
if (!(vStatCode & S_INSEARCH)) {
|
|
ScrLock (1);
|
|
Update_head ();
|
|
vDate [ST_MEMORY] = 'M';
|
|
dis_str ((Uchar)(vWidth - ST_ADJUST), 0, vDate);
|
|
ScrUnLock ();
|
|
}
|
|
}
|
|
break; // Nothing to do. Wait
|
|
}
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
#if _MSC_FULL_VER >= 13008827
|
|
#pragma warning(pop)
|
|
#endif
|
|
|
|
|
|
// WARNING: Microsoft Confidential!!!
|
|
|
|
void
|
|
NewFile ()
|
|
{
|
|
char s [60];
|
|
char h, c;
|
|
SYSTEMTIME SystemTime;
|
|
FILETIME LocalFileTime;
|
|
|
|
long *pLine;
|
|
HANDLE TempHandle;
|
|
|
|
struct Block **pBlk, **pBlkCache;
|
|
|
|
|
|
if (vFhandle)
|
|
CloseHandle (vFhandle);
|
|
|
|
|
|
vFType = 0;
|
|
vCurOffset = 0L;
|
|
|
|
// WARNING: Microsoft Confidential!!!
|
|
|
|
strcpy (s, "Listing ");
|
|
strcpy (s+8, vpFname);
|
|
|
|
// Design change per DougHo.. open files in read-only deny-none mode.
|
|
|
|
vFhandle = CreateFile( vpFlCur->fname,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ|FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL );
|
|
|
|
if (vFhandle == (HANDLE)(-1)) {
|
|
if (vpFlCur->prev == NULL && vpFlCur->next == NULL) {
|
|
// Only one file specified?
|
|
printf ("Could not open file '%Fs': %s",
|
|
(CFP) vpFlCur->fname, GetErrorCode( GetLastError() ));
|
|
|
|
CleanUp();
|
|
ExitProcess(0);
|
|
}
|
|
vFhandle = 0; // Error. Set externals to "safe"
|
|
vFInfo.nFileSizeLow = (unsigned)-1L; // settings. Flag error by setting
|
|
vNLine = 1; // file_size = -1
|
|
vLastLine = NOLASTLINE;
|
|
vDirOffset = vTopLine = 0L;
|
|
SetLoffset(0L);
|
|
|
|
memset (vprgLineTable, 0, sizeof (long *) * MAXTPAGE);
|
|
vprgLineTable[0] = (LFP) alloc_page ();
|
|
if (!vprgLineTable[0]) {
|
|
return;
|
|
}
|
|
vprgLineTable[0][0] = 0L; // 1st line always starts @ 0
|
|
|
|
strncpy (vDate, GetErrorCode( GetLastError() ), 20);
|
|
vDate[20] = 0;
|
|
return ;
|
|
}
|
|
|
|
TempHandle = FindFirstFile( vpFlCur->fname,
|
|
&vFInfo );
|
|
if( TempHandle == (HANDLE)(-1) ){
|
|
ckerr (GetLastError(), "FindFirstFile");
|
|
if (!FindClose( TempHandle ))
|
|
ckerr (GetLastError(), "FindCloseFile");
|
|
}
|
|
|
|
FileTimeToLocalFileTime( &(vFInfo.ftLastWriteTime), &LocalFileTime );
|
|
FileTimeToSystemTime( &LocalFileTime, &SystemTime );
|
|
h = (char)SystemTime.wHour;
|
|
c = 'a';
|
|
if (h >= 12) {
|
|
c = 'p'; // pm
|
|
if (h > 12) // convert 13-23 --> 1pm-11pm
|
|
h -= 12;
|
|
}
|
|
if (h == 0) // convert 0 --> 12am
|
|
h = 12;
|
|
|
|
sprintf (vDate, "%c%c %c%c%c%c %2d/%02d/%02d %2d:%02d%c",
|
|
// File is in memory
|
|
// Search is set for mult files
|
|
|
|
vStatCode & S_MFILE ? '*' : ' ', // File is in memory
|
|
vFType & 0x8000 ? 'N' : ' ', // Network
|
|
vFInfo.dwFileAttributes & FILE_ATTRIBUTE_NORMAL ? 'R' : ' ', // Readonly
|
|
vFInfo.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN ? 'H' : ' ', // Hidden
|
|
vFInfo.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM ? 'S' : ' ', // System
|
|
' ', // Vio
|
|
SystemTime.wMonth,
|
|
SystemTime.wDay,
|
|
SystemTime.wYear,
|
|
h,
|
|
SystemTime.wMinute,
|
|
c);
|
|
|
|
pBlkCache = &vpBCache;
|
|
if (CompareFileTime( &vFInfo.ftLastWriteTime, &vpFlCur->FileTime ) != 0) {
|
|
vpFlCur->NLine = 1L; // Something has changed.
|
|
vpFlCur->LastLine = NOLASTLINE; // Scrap the old info, and
|
|
vpFlCur->HighTop = -1; // start over
|
|
vpFlCur->TopLine = 0L;
|
|
vpFlCur->Loffset = vpFlCur->SlimeTOF;
|
|
|
|
FreePages (vpFlCur);
|
|
memset (vpFlCur->prgLineTable, 0, sizeof (long *) * MAXTPAGE);
|
|
vpFlCur->FileTime = vFInfo.ftLastWriteTime;
|
|
pBlkCache = &vpBFree; // Move blks to free list, not cache list
|
|
}
|
|
|
|
// Restore last known information
|
|
|
|
vTopLine = vpFlCur->TopLine;
|
|
SetLoffset(vpFlCur->Loffset);
|
|
vLastLine = vpFlCur->LastLine;
|
|
vNLine = vpFlCur->NLine;
|
|
vOffTop = 0;
|
|
if (vpFlCur->Wrap)
|
|
vWrap = vpFlCur->Wrap;
|
|
|
|
memcpy (vprgLineTable, vpFlCur->prgLineTable, sizeof (long *) * MAXTPAGE);
|
|
|
|
if (vLastLine == NOLASTLINE) {
|
|
pLine = vprgLineTable [vNLine/PLINES] + vNLine % PLINES;
|
|
}
|
|
|
|
if (vprgLineTable[0] == NULL) {
|
|
vprgLineTable[0] = (LFP) alloc_page ();
|
|
if (!vprgLineTable[0]) {
|
|
return;
|
|
}
|
|
vprgLineTable[0][0] = vpFlCur->SlimeTOF;
|
|
}
|
|
|
|
vDirOffset = vprgLineTable[vTopLine/PLINES][vTopLine%PLINES];
|
|
vDirOffset -= vDirOffset % ((long)BLOCKSIZE);
|
|
|
|
|
|
// Adjust buffers..
|
|
// Move cur buffers to other list
|
|
// Move cache buffers to other list
|
|
// Scan other list for cache blks, and move to cache (or free) list
|
|
|
|
if (vpHead) {
|
|
vpTail->next = vpBOther; // move them into the other
|
|
vpBOther = vpHead; // list
|
|
vpHead = NULL;
|
|
}
|
|
|
|
pBlk = &vpBCache;
|
|
while (*pBlk)
|
|
MoveBlk (pBlk, &vpBOther) ;
|
|
|
|
pBlk = &vpBOther;
|
|
while (*pBlk) {
|
|
if ((*pBlk)->pFile == vpFlCur)
|
|
MoveBlk (pBlk, pBlkCache);
|
|
else pBlk = &(*pBlk)->next;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// ReadDirect - Moves to the direct position in the file
|
|
//
|
|
// First check to see if start of buffers have direct position file,
|
|
// if so then do nothing. If not, clear all buffers and start
|
|
// reading blocks.
|
|
|
|
void
|
|
ReadDirect (
|
|
long offset
|
|
)
|
|
{
|
|
WaitForSingleObject(vSemBrief, WAITFOREVER);
|
|
ResetEvent(vSemBrief);
|
|
|
|
if (vpHead) {
|
|
vpTail->next = vpBCache; // move them into the cache
|
|
vpBCache = vpHead; // list
|
|
}
|
|
|
|
vpTail = vpHead = vpCur = alloc_block (offset);
|
|
vpHead->next = vpTail->prev = NULL;
|
|
vCntBlks = 1;
|
|
|
|
// Freeing is complete, now read in the first block.
|
|
// and process lines.
|
|
|
|
ReadBlock (vpHead, offset);
|
|
|
|
// maybe it fixes the bug
|
|
|
|
vpBlockTop = vpHead;
|
|
|
|
if (GetLoffset() <= vpHead->offset)
|
|
add_more_lines (vpHead, NULL);
|
|
|
|
SetEvent (vSemBrief);
|
|
SetEvent (vSemMoreData); // Signal another BLK read
|
|
}
|
|
|
|
|
|
|
|
// ReadNext - To read further into file
|
|
|
|
void
|
|
ReadNext ()
|
|
{
|
|
struct Block *pt;
|
|
long offset;
|
|
|
|
if (vpTail->flag == F_EOF) {
|
|
// No next to get, Trip
|
|
SetEvent (vSemMoreData); // moredata just in case
|
|
return; // t1 has blocked on it
|
|
// No next to get, Trip
|
|
}
|
|
offset = vpTail->offset+BLOCKSIZE;
|
|
|
|
// Get a block
|
|
|
|
if (vCntBlks == vMaxBlks) {
|
|
WaitForSingleObject(vSemBrief, WAITFOREVER);
|
|
ResetEvent(vSemBrief);
|
|
if (vpHead == vpCur) {
|
|
SetEvent (vSemBrief);
|
|
if ((GetLoffset() > vpTail->offset) && (GetLoffset() <= (vpTail->offset + BLOCKSIZE))) {
|
|
offset = GetLoffset();
|
|
}
|
|
ReadDirect (offset);
|
|
return;
|
|
}
|
|
pt = vpHead;
|
|
vpHead = vpHead->next;
|
|
vpHead->prev = NULL;
|
|
SetEvent (vSemBrief);
|
|
} else
|
|
pt = alloc_block (offset);
|
|
|
|
pt->next = NULL;
|
|
|
|
// Before linking record into chain, or signaling MoreData
|
|
// line info is processed
|
|
|
|
ReadBlock (pt, offset);
|
|
if (GetLoffset() <= pt->offset)
|
|
add_more_lines (pt, vpTail);
|
|
|
|
WaitForSingleObject(vSemBrief, WAITFOREVER);
|
|
ResetEvent(vSemBrief); // Link in new
|
|
vpTail->next = pt; // block, then
|
|
pt->prev = vpTail; // signal
|
|
vpTail = pt;
|
|
SetEvent (vSemBrief);
|
|
SetEvent (vSemMoreData); // Signal another BLK read
|
|
}
|
|
|
|
void
|
|
add_more_lines (
|
|
struct Block *cur,
|
|
struct Block *prev
|
|
)
|
|
{
|
|
char *pData;
|
|
long *pLine;
|
|
Uchar LineLen;
|
|
Uchar c;
|
|
unsigned LineIndex;
|
|
unsigned DataIndex;
|
|
enum{ CT_ANK, CT_LEAD, CT_TRAIL } charType = CT_ANK;
|
|
BOOL fLastBlock;
|
|
static UINT cp = 0;
|
|
long xNLine;
|
|
|
|
// doesn't work w/ tabs... it should count the line len
|
|
// with a different param, and figure in the TABs
|
|
|
|
if (vLastLine != NOLASTLINE)
|
|
return;
|
|
|
|
if (vNLine/PLINES >= MAXTPAGE) {
|
|
puts("Sorry, This file is too big for LIST to handle - MAXTPAGE limit exceeded\n");
|
|
CleanUp();
|
|
ExitProcess(0);
|
|
}
|
|
|
|
// Find starting data position
|
|
|
|
if (GetLoffset() < cur->offset) {
|
|
DataIndex = (unsigned)(BLOCKSIZE - (GetLoffset() - prev->offset));
|
|
pData = prev->Data + BLOCKSIZE - DataIndex;
|
|
fLastBlock = FALSE;
|
|
} else {
|
|
DataIndex = cur->size; // Use cur->size, in case EOF
|
|
pData = cur->Data;
|
|
fLastBlock = TRUE;
|
|
}
|
|
|
|
// Get starting line length table position
|
|
|
|
LineIndex = (unsigned)(vNLine % PLINES);
|
|
pLine = vprgLineTable [vNLine / PLINES] + LineIndex;
|
|
LineLen = 0;
|
|
|
|
if (cp==0) {
|
|
cp = GetConsoleCP();
|
|
}
|
|
|
|
// Look for lines in the file
|
|
|
|
for (; ;) {
|
|
c = *(pData++);
|
|
|
|
switch (cp) {
|
|
case 932:
|
|
if( charType != CT_LEAD )
|
|
charType = IsDBCSLeadByte(c) ? CT_LEAD : CT_ANK;
|
|
else
|
|
charType = CT_TRAIL;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (--DataIndex == 0) {
|
|
if (fLastBlock)
|
|
break; // Last block to scan?
|
|
DataIndex = cur->size; // No, move onto next
|
|
pData = cur->Data; // Block of data
|
|
fLastBlock = TRUE;
|
|
}
|
|
|
|
LineLen++;
|
|
|
|
if ((c == '\n') ||
|
|
(c == '\r') ||
|
|
(LineLen == vWrap) ||
|
|
((LineLen == vWrap-1) && (charType != CT_LEAD) && IsDBCSLeadByte(*pData))
|
|
)
|
|
{
|
|
// Got a line. Check for CR/LF sequence, then record
|
|
// it's length.
|
|
|
|
if ( (c == '\n' && *pData == '\r') ||
|
|
(c == '\r' && *pData == '\n'))
|
|
{
|
|
LineLen++;
|
|
pData++;
|
|
if (--DataIndex == 0) {
|
|
if (fLastBlock)
|
|
break;
|
|
DataIndex = cur->size;
|
|
pData = cur->Data;
|
|
fLastBlock = TRUE;
|
|
}
|
|
}
|
|
|
|
SetLoffset(GetLoffset() + LineLen);
|
|
*(pLine++) = GetLoffset();
|
|
LineLen = 0;
|
|
vNLine++;
|
|
if (++LineIndex >= PLINES) { // Overflowed table
|
|
LineIndex = 0;
|
|
vprgLineTable[vNLine / PLINES] = pLine = (LFP) alloc_page();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Was last line just processed?
|
|
// ... 0 len lines past EOF
|
|
|
|
if (cur->flag & F_EOF) {
|
|
if (LineLen) {
|
|
SetLoffset(GetLoffset() + LineLen);
|
|
*(pLine++) = GetLoffset();
|
|
vNLine++;
|
|
LineIndex++;
|
|
}
|
|
|
|
vLastLine = vNLine-1;
|
|
xNLine = vNLine;
|
|
for (c=0; c<MAXLINES; c++) {
|
|
xNLine++;
|
|
if (++LineIndex >= PLINES) {
|
|
LineIndex = 0;
|
|
vprgLineTable[xNLine / PLINES] = pLine = (LFP) alloc_page();
|
|
}
|
|
*(pLine++) = GetLoffset();
|
|
}
|
|
|
|
// Free the memory we don't need
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// ReadPrev - To read backwards into file
|
|
|
|
void
|
|
ReadPrev ()
|
|
{
|
|
struct Block *pt;
|
|
long offset;
|
|
|
|
if (vpHead->offset == 0L) { // No next to get, Trip
|
|
SetEvent (vSemMoreData); // moredata just in case
|
|
return; // t1 has blocked on it
|
|
}
|
|
if (vpHead->offset == 0L) { // No next to get, Trip
|
|
return; // t1 has blocked on it
|
|
}
|
|
offset = vpHead->offset-BLOCKSIZE;
|
|
|
|
// Get a block
|
|
|
|
if (vCntBlks == vMaxBlks) {
|
|
WaitForSingleObject(vSemBrief, WAITFOREVER);
|
|
ResetEvent(vSemBrief);
|
|
if (vpHead == vpCur) {
|
|
SetEvent (vSemBrief);
|
|
ReadDirect (offset);
|
|
return;
|
|
}
|
|
pt = vpTail;
|
|
vpTail = vpTail->prev;
|
|
vpTail->next = NULL;
|
|
SetEvent (vSemBrief);
|
|
} else
|
|
pt = alloc_block (offset);
|
|
|
|
pt->prev = NULL;
|
|
|
|
ReadBlock (pt, offset);
|
|
WaitForSingleObject(vSemBrief, WAITFOREVER);
|
|
ResetEvent(vSemBrief); // Link in new
|
|
vpHead->prev = pt; // block, then
|
|
pt->next = vpHead; // signal
|
|
vpHead = pt;
|
|
SetEvent (vSemBrief);
|
|
SetEvent (vSemMoreData); // Signal another BLK read
|
|
}
|
|
|
|
|
|
|
|
// ReadBlock - Read in one block
|
|
|
|
void
|
|
ReadBlock (
|
|
struct Block *pt,
|
|
long offset
|
|
)
|
|
{
|
|
long l;
|
|
DWORD dwSize;
|
|
|
|
|
|
if (pt->offset == offset)
|
|
return;
|
|
|
|
pt->offset = offset;
|
|
|
|
if (vFhandle == 0) { // No file?
|
|
pt->size = 1;
|
|
pt->flag = F_EOF;
|
|
pt->Data[0] = '\n';
|
|
return;
|
|
}
|
|
|
|
if (offset != vCurOffset) {
|
|
l = SetFilePointer( vFhandle, offset, NULL, FILE_BEGIN );
|
|
if (l == -1) {
|
|
ckerr (GetLastError(), "SetFilePointer");
|
|
}
|
|
}
|
|
if( !ReadFile (vFhandle, pt->Data, BLOCKSIZE, &dwSize, NULL) ) {
|
|
ckerr ( GetLastError(), "ReadFile" );
|
|
}
|
|
pt->size = (USHORT) dwSize;
|
|
if (pt->size != BLOCKSIZE) {
|
|
pt->Data[pt->size++] = '\n';
|
|
memset (pt->Data + pt->size, 0, BLOCKSIZE-pt->size);
|
|
pt->flag = F_EOF;
|
|
vCurOffset += pt->size;
|
|
} else {
|
|
pt->flag = 0;
|
|
vCurOffset += BLOCKSIZE;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
SyncReader ()
|
|
{
|
|
vReaderFlag = F_SYNC;
|
|
SetEvent (vSemReader);
|
|
WaitForSingleObject(vSemSync, WAITFOREVER);
|
|
ResetEvent(vSemSync);
|
|
}
|
|
|
|
|
|
// These functions are used for the call to HexEdit()
|
|
|
|
NTSTATUS
|
|
fncRead (
|
|
HANDLE h,
|
|
DWORD loc,
|
|
char *data,
|
|
DWORD len,
|
|
ULONG *ploc
|
|
)
|
|
{
|
|
DWORD l, br;
|
|
|
|
l = SetFilePointer (h, loc, NULL, FILE_BEGIN);
|
|
if (l == -1)
|
|
return GetLastError();
|
|
|
|
if (!ReadFile (h, data, len, &br, NULL))
|
|
return GetLastError();
|
|
|
|
return (br != len ? ERROR_READ_FAULT : 0);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
fncWrite (
|
|
HANDLE h,
|
|
DWORD loc,
|
|
char *data,
|
|
DWORD len,
|
|
ULONG ploc
|
|
)
|
|
{
|
|
DWORD l, bw;
|
|
|
|
l = SetFilePointer (h, loc, NULL, FILE_BEGIN);
|
|
if (l == -1)
|
|
return GetLastError();
|
|
|
|
if (!WriteFile (h, data, len, &bw, NULL))
|
|
return GetLastError();
|
|
|
|
return (bw != len ? ERROR_WRITE_FAULT : 0);
|
|
}
|
|
|
|
|
|
long CurrentLineOffset;
|
|
|
|
long GetLoffset() {
|
|
return(CurrentLineOffset);
|
|
}
|
|
|
|
void SetLoffset(long l) {
|
|
CurrentLineOffset = l;
|
|
return;
|
|
}
|