Windows2000/private/windbg64/windbg/document.c
2020-09-30 17:12:32 +02:00

2264 lines
57 KiB
C

/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
document.c
Abstract:
This file contains the code which deals with the underlying document
file.
Author:
Jim Schaad (jimsch)
Griffith Wm. Kadnier (v-griffk) 1-Nov-1992
Environment:
Win32 - User
--*/
#include "precomp.h"
#pragma hdrstop
#define ROREGION(d,X,Y) ((d)->RORegionSet && ((Y)<(d)->RoY2 || ((Y)==(d)->RoY2 && (X)<(d)->RoX2)))
//Prototypes
void NEAR PASCAL ReadOnlyBeep (void);
LPBLOCKDEF NEAR PASCAL FreeBlock (int doc, LPBLOCKDEF pCurBlock);
void NEAR PASCAL ModifiedDoc (int doc);
BOOL NEAR PASCAL FindInsertSpace (int doc, LPBLOCKDEF *pCurBlock, LPLINEREC *pCurLine, LPSTR pBlockEnd, int sizeExpansion);
BOOL NEAR PASCAL CancelDelete (int doc);
BOOL NEAR PASCAL PartialLineDelete (int doc, LPBLOCKDEF *pCurBlock, LPLINEREC *pCurLine, int *totalSize, int col1, int col2, BOOL moveToNextLine);
BOOL NEAR PASCAL FullLineDelete (int doc, LPBLOCKDEF *pCurBlock, LPLINEREC *pCurLine, int *totalSize);
BOOL NEAR PASCAL JoinLines (int doc, LPBLOCKDEF pCurBlock, LPLINEREC pCurLine, int *totalSize, int lineNb, int col1);
/*
Document Structure in memory
---------
(Use TABULATION = 3 if drawing looks bad)
- Blocks are allocated using malloc
Line status (tags,
----------> error, breakpoint, <-
| in C comment, ...) |
| |
| Previous line | ---------> Previous line
| --------> length (in previous | | length
| | Block for Line 0) | |
| | | |
| | ------> Line Length | | -------> Line length
| | | | | |
| | | --> Chars | | | | ----> Chars
| | | | | | | | |
| | | | v | | | |
| | | | | ... | | | |
| | | | | |_|_|_|_________| |_|_|...__| |_|_|_|____| <-Unused->| | | |
| | | | | Line 0 Block n Line...1 Line m Block n space | | | |
__| |_|_|_|_________________________...____________________________| |_|_|__
| | | | Block n ^ |
| | | | | |
v | | ---> Offset of Last line in block --- v
| |
Block | | Block
n - 1 | -----> Address of Block n + 1 (0 if last block) n + 1
|
|
-------> Address of Block n - 1 (0 if first block)
*/
static BOOL fReadOnlyErrorStatus = FALSE;
static BOOL fReadOnlyBeepEnabled = TRUE;
void NEAR PASCAL
ReadOnlyBeep(
void
)
/*++
Routine Description:
Report a readonly error condition; set flag and
beep if the beep flag is set.
Arguments:
none
Return Value:
none
--*/
{
fReadOnlyErrorStatus = TRUE;
if (fReadOnlyBeepEnabled) {
MessageBeep(0);
}
}
void FAR PASCAL
EnableReadOnlyBeep(
BOOL f
)
/*++
Routine Description:
Set or clear the beep flag. Always clears the error status flag.
Arguments:
f - supplies new flag value
Return Value:
none
--*/
{
fReadOnlyBeepEnabled = (f != 0);
fReadOnlyErrorStatus = FALSE;
}
BOOL FAR PASCAL
QueryReadOnlyError(
void
)
/*++
Routine Description:
Examine the read only error status flag. Status flag is cleared
after examining the value.
Arguments:
None
Return Value:
Value of readonly error status - TRUE or FALSE.
--*/
{
BOOL f = fReadOnlyErrorStatus;
fReadOnlyErrorStatus = FALSE;
return f;
}
LPSTR FAR PASCAL
DocAlloc(
WORD bytes
)
{
PSTR psz = (PSTR) malloc(bytes);
if (psz) {
ZeroMemory(psz, bytes);
}
return psz;
}
BOOL FAR PASCAL
DocFree(
LPVOID lpv
)
{
free(lpv);
return TRUE;
}
//Allocates and initialize a block, update doc pointers
BOOL FAR PASCAL
AllocateBlock(
LPBLOCKDEF pPrevBlock,
LPBLOCKDEF pNextBlock,
LPBLOCKDEF *pNewBlock
)
{
//Allocate memory
*pNewBlock = (LPBLOCKDEF)DocAlloc(sizeof(BLOCKDEF));
if (*pNewBlock == NULL) {
return ErrorBox(SYS_Allocate_Memory);
}
//Do initialization work
(*pNewBlock)->NextBlock = pNextBlock;
(*pNewBlock)->PrevBlock = pPrevBlock;
(*pNewBlock)->LastLineOffset = 0;
return TRUE;
}
//Free current block, link previous and next blocks, update doc pointers
LPBLOCKDEF NEAR PASCAL
FreeBlock(
int doc,
LPBLOCKDEF pCurBlock
)
{
LPBLOCKDEF nextBlock = pCurBlock->NextBlock;
//Link previous block to next block
if (pCurBlock->PrevBlock == NULL) {
Docs[doc].FirstBlock = nextBlock;
} else {
pCurBlock->PrevBlock->NextBlock = nextBlock;
}
if (nextBlock == NULL) {
Docs[doc].LastBlock = pCurBlock->PrevBlock;
} else {
pCurBlock->NextBlock->PrevBlock = pCurBlock->PrevBlock;
}
//Release memory
if (!DocFree((LPSTR)pCurBlock)) {
InternalErrorBox(SYS_Free_Memory);
}
return nextBlock;
}
/*** InitializeDocument
** Synopsis:
** void = InitializeDocument(void)
** Entry:
** none
** Returns:
** Nothing
** Description:
** Initialize global variables used by the module
*/
void
InitializeDocument(
void
)
{
UINT i;
for (i = 0; i < MAX_DOCUMENTS; i++) {
Docs[i].FirstView = -1;
}
for (i = 0; i < MAX_VIEWS; i++) {
Views[i].Doc = -1;
}
} /* InitializeDocument() */
/*** FirstLine
** Synopsis:
** bool = FirstLine(doc, pl, lineNb, pb)
** Entry:
** doc - Current document index
** pl - Place where the line is to be stored
** lineNb - pointer to line to be searched (incremented after search)
** pb - pointer to block pointer to be returned
** Returns:
** Description:
*/
BOOL FAR PASCAL
FirstLine (
int doc,
LPLINEREC *pl,
long *lineNb,
LPBLOCKDEF *pb
)
{
LPLINEREC stop;
int l;
LPDOCREC d = &Docs[doc];
// kcarlos
// BUGBUG
// This simple solves the symptom, but not the cause.
// Assert(*lineNb < d->NbLines);
if (*lineNb >= d->NbLines) {
*lineNb = d->NbLines - 1;
}
//Exit if line was the last loaded
if (*lineNb == d->CurrentLine) {
*pb = d->CurrentBlock;
*pl = (LPLINEREC)((LPSTR)(*pb)->Data + d->CurrentLineOffset);
(*lineNb)++;
//Expand line
ExpandTabs(pl);
pcl = *pl;
pcb = *pb;
return TRUE;
}
//First evaluate from which block do we start
if (*lineNb <= (d->CurrentLine >> 1)) {
//Go up from first line
*pb = d->FirstBlock;
l = *lineNb;
*pl = (LPLINEREC)((*pb)->Data);
stop = (LPLINEREC)((*pb)->Data + (*pb)->LastLineOffset);
while (l--) {
//Get next line
if (*pl < stop) {
//We are below the last line of the block,
// get just the next line
*pl = (LPLINEREC) ((LPSTR)*pl + (*pl)->Length);
} else {
//We are at the last line of the block. Load the next block
//and initialize line
Assert(*pl == stop);
Assert((*pb)->NextBlock != NULL);
*pb = (*pb)->NextBlock;
*pl = (LPLINEREC)((*pb)->Data);
stop = (LPLINEREC)((*pb)->Data + (*pb)->LastLineOffset);
}
}
} else if (*lineNb <= d->CurrentLine) {
//Go down from current line
*pb = d->CurrentBlock;
l = d->CurrentLine - *lineNb;
*pl = (LPLINEREC)((*pb)->Data + d->CurrentLineOffset);
stop = (LPLINEREC)((*pb)->Data);
while (l--) {
if (*pl > stop) {
*pl = (LPLINEREC) ((LPSTR)*pl - (*pl)->PrevLength);
} else {
Assert(*pl == stop);
Assert((*pb)->PrevBlock != NULL);
*pb = (*pb)->PrevBlock;
*pl = (LPLINEREC)((*pb)->Data + (*pb)->LastLineOffset);
stop = (LPLINEREC)((*pb)->Data);
}
}
// Kim Peterson
// BUGBUG: Original expression without parens was always true
// may be related to previous BUG
} else if (*lineNb <= (d->CurrentLine + ((d->NbLines-d->CurrentLine) >> 1))) {
//Go up from current line
*pb = d->CurrentBlock;
l = *lineNb - d->CurrentLine;
*pl = (LPLINEREC)((*pb)->Data + d->CurrentLineOffset);
stop = (LPLINEREC)((*pb)->Data + (*pb)->LastLineOffset);
while (l--) {
if (*pl < stop) {
*pl = (LPLINEREC) ((LPSTR)*pl + (*pl)->Length);
} else {
Assert(*pl == stop);
Assert((*pb)->NextBlock != NULL);
*pb = (*pb)->NextBlock;
*pl = (LPLINEREC)((*pb)->Data);
stop = (LPLINEREC)((*pb)->Data + (*pb)->LastLineOffset);
}
}
}
else {
// Go down from last line
*pb = d->LastBlock;
l = d->NbLines - *lineNb -1;
*pl = (LPLINEREC)((*pb)->Data + (*pb)->LastLineOffset);
stop = (LPLINEREC)((*pb)->Data);
while (l--) {
if (*pl > stop) {
*pl = (LPLINEREC) ((LPSTR)*pl - (*pl)->PrevLength);
} else {
Assert(*pl == stop);
Assert((*pb)->PrevBlock != NULL);
*pb = (*pb)->PrevBlock;
*pl = (LPLINEREC)((*pb)->Data + (*pb)->LastLineOffset);
stop = (LPLINEREC)((*pb)->Data);
}
}
}
//Increment line count
(*lineNb)++;
//Expand line
ExpandTabs(pl);
pcl = *pl;
pcb = *pb;
return TRUE;
} /* FirstLine() */
/*** NextLine
** Synopsis:
** bool = NextLine(doc, pl, lineno, bl, pb)
** Entry:
** Returns:
** Description:
*/
BOOL FAR PASCAL
NextLine(
int doc,
LPLINEREC *pl,
long *lineNb,
LPBLOCKDEF *pb
)
{
LPDOCREC d = &Docs[doc];
if (*lineNb >= d->NbLines) {
*lineNb = LAST_LINE;
return TRUE;
}
Assert(*lineNb >= 0);
Assert(((LPSTR)*pl - (LPSTR)(*pb)->Data) <= (*pb)->LastLineOffset);
//Return next line
if ((LPSTR)*pl == (LPSTR)((*pb)->Data + (*pb)->LastLineOffset)) {
//We are at the last line of the block. Load next block
Assert((*pb)->NextBlock != NULL);
*pb = (*pb)->NextBlock;
//First line of next block
*pl = (LPLINEREC)((*pb)->Data);
AssertAligned( *pl );
}
else {
int i;
//Get pointer to next line
i = (*pl)->Length;
#ifdef ALIGN
i = (i + 3) & ~3;
#endif
*pl = (LPLINEREC)((LPSTR)*pl + i);
AssertAligned( *pl );
}
(*lineNb)++;
//Expand line
ExpandTabs(pl);
pcl = *pl;
pcb = *pb;
return TRUE;
} /* NextLine() */
/*** PreviousLine
*/
BOOL FAR PASCAL
PreviousLine(
int doc,
LPLINEREC *pl,
long lineNb,
LPBLOCKDEF *pb
)
{
LPDOCREC d = &Docs[doc];
if (lineNb <= 0) {
return TRUE;
}
Assert(lineNb < d->NbLines);
Assert(((LPSTR)(*pl)-(LPSTR)(*pb)->Data) <= (*pb)->LastLineOffset);
//Return previous line
if ((LPSTR)(*pl) == (LPSTR)((*pb)->Data)) {
//We are at the first line of the block. Load previous block
Assert((*pb)->PrevBlock != NULL);
*pb = (*pb)->PrevBlock;
//Last line of previous block
*pl = (LPLINEREC)((LPSTR)(*pb)->Data + (*pb)->LastLineOffset);
} else {
//Get pointer to previous line
*pl = (LPLINEREC)((LPSTR)(*pl) - (*pl)->PrevLength);
}
//Expand line
ExpandTabs(pl);
pcl = *pl;
pcb = *pb;
return TRUE;
} /* PreviousLine() */
/*** CloseLine
** Synopsis:
** void = CloseLine()
** Entry:
** Returns:
** Description:
*/
void FAR PASCAL
CloseLine (
int doc,
LPLINEREC *pl,
long lineNb,
LPBLOCKDEF *pb
)
{
LPDOCREC d = &Docs[doc];
//Save current line infos
d->CurrentBlock = *pb;
d->CurrentLine = lineNb - 1;
d->CurrentLineOffset = (int) ((LPSTR)(*pl) - (LPSTR)(*pb)->Data);
return;
} /* CloseLine() */
/*** FirstNonBlank
*/
int FAR PASCAL
FirstNonBlank(
int doc,
long line
)
{
LPLINEREC pl;
LPBLOCKDEF pb;
register int x;
if (!FirstLine(doc, &pl, &line, &pb)) {
return FALSE;
}
CloseLine(doc, &pl, line, &pb);
x = 0;
while (x < elLen && el[x] == ' ') {
x++;
}
if (x >= (elLen)) {
x = 0;
}
return x;
} /* FirstNonBlank() */
void FAR PASCAL
CheckSyntax(
int doc
)
{
LPDOCREC d = &Docs[doc];
LPBLOCKDEF pCurBlock;
LPLINEREC pCurLine;
char prevCh;
char ch;
int i;
long line = d->lineTop;
int savedLineBottom;
int curStatus, prevStatus;
int curLevel, prevLevel;
if (d->lineBottom >= d->NbLines) {
d->lineBottom--;
}
Assert(d->lineBottom < d->NbLines);
savedLineBottom = d->lineBottom;
//Load previous line to get previous line status
if (line > 0) {
line--;
if (!FirstLine(doc, &pCurLine, &line, &pCurBlock)) {
return;
}
//Compute previous line comment level and status
prevStatus = pCurLine->Status & COMMENT_LINE;
if (prevStatus == COMMENT_LINE) {
prevLevel = 1;
} else {
for (i = 0, prevCh = ' ', prevLevel = 0;
i < pCurLine->Length - LHD; i++) {
if (IsDBCSLeadByte(pCurLine->Text[i]) && pCurLine->Text[i+1]) {
prevCh = ' ';
i++;
continue;
}
ch = pCurLine->Text[i];
if (prevCh == '/' && ch == '*') {
prevLevel++;
}
if (prevCh == '*' && ch == '/') {
prevLevel = -1;
}
prevCh = ch;
}
}
if (!NextLine(doc, &pCurLine, &line, &pCurBlock)) {
return;
}
} else {
Assert(line == 0);
prevStatus = prevLevel = 0;
if (!FirstLine(doc, &pCurLine, &line, &pCurBlock)) {
return;
}
}
//Scan now every line between lineTop and lineBottom and possibly
//extend lineBottom if needed
while (TRUE) {
//Compute current line comment level and status
curStatus = pCurLine->Status & COMMENT_LINE;
for (i = 0, prevCh = ' ', curLevel = 0; i < pCurLine->Length - LHD;
i++) {
if (IsDBCSLeadByte(pCurLine->Text[i]) && pCurLine->Text[i+1]) {
prevCh = ' ';
i++;
continue;
}
ch = pCurLine->Text[i];
if (prevCh == '/' && ch == '*') {
if (i <= 1 || pCurLine->Text[i-2] != '/') {
curLevel++;
}
}
if (prevCh == '*' && ch == '/') {
curLevel = -1;
}
prevCh = ch;
}
//Impossible combination
Assert(prevStatus == 0 || prevLevel >= 0);
//Set or reset multiline status
if (prevStatus == 0 && prevLevel <= 0) {
if (curLevel > 0) {
SET(pCurLine->Status, COMMENT_LINE);
} else {
RESET(pCurLine->Status, COMMENT_LINE);
}
} else if (curLevel >= 0) {
SET(pCurLine->Status, COMMENT_LINE);
} else {
RESET(pCurLine->Status, COMMENT_LINE);
}
//Current line info is now previous
prevStatus = pCurLine->Status & COMMENT_LINE;
//Check stop conditions
if (line > d->lineBottom) {
if (curStatus == prevStatus) {
break;
} else {
d->lineBottom++;
}
}
//Current level is previous one
prevLevel = curLevel;
//Stop if at end of doc
if (line >= d->NbLines) {
break;
}
//Return next line
if ((LPSTR)pCurLine ==
(LPSTR)(pCurBlock->Data + pCurBlock->LastLineOffset)) {
pCurBlock = pCurBlock->NextBlock;
pCurLine = (LPLINEREC)(pCurBlock->Data);
}
else {
pCurLine = (LPLINEREC)((LPSTR)pCurLine + pCurLine->Length);
}
line++;
}
if (!syntaxColors) {
d->lineBottom = savedLineBottom;
}
CloseLine(doc, &pCurLine, line, &pCurBlock);
return;
} /* CheckSyntax() */
/*** ModifiedDoc
** Synopsis:
** void = ModifiedDoc(doc)
** Entry:
** doc - index of document which has been modified
** Returns:
** Nothing:
** Description:
** Marks the document as having been modified and updates the
** title if it has not previously been modified
*/
void NEAR PASCAL
ModifiedDoc(
int doc
)
{
//Document is modified
if (!Docs[doc].ismodified) {
Docs[doc].ismodified = TRUE;
RefreshWindowsTitle(doc);
}
return;
} /* ModifiedDoc() */
void FAR PASCAL
ExpandTabs(
LPLINEREC *pl
)
{
register int i = 0;
register int j = 0;
int len = (*pl)->Length - LHD;
LPSTR pc = (*pl)->Text;
memset(el, ' ', MAX_USER_LINE * 2);
while (i < len) {
if (IsDBCSLeadByte((BYTE)pc[i])) {
el[j++] = pc[i++];
el[j++] = pc[i++];
} else
if (pc[i] == TAB) {
j += g_contGlobalPreferences_WkSp.m_nTabSize - (j % g_contGlobalPreferences_WkSp.m_nTabSize);
i++;
}
else {
el[j++] = pc[i++];
}
}
Assert(j <= MAX_USER_LINE);
elLen = j;
}
int FAR PASCAL
AlignToTabs(
int editCol,
int len,
LPSTR pc
)
{
register int col, realCol;
col = realCol = 0;
while (realCol < editCol) {
if (col >= len) {
col += (editCol - realCol);
break;
}
if (IsDBCSLeadByte(pc[col])) {
realCol += 2;
} else if (pc[col] == TAB) {
realCol += g_contGlobalPreferences_WkSp.m_nTabSize - (realCol % g_contGlobalPreferences_WkSp.m_nTabSize);
} else {
realCol++;
}
if (realCol <= editCol) {
if (IsDBCSLeadByte(pc[col])) {
col += 2;
} else {
col++;
}
}
}
return col;
} /* AlignToTabs() */
BOOL NEAR PASCAL
FindInsertSpace(
int doc,
LPBLOCKDEF *pCurBlock,
LPLINEREC *pCurLine,
LPSTR pBlockEnd,
int sizeExpansion
)
{
LPBLOCKDEF pNextBlock;
LPLINEREC pNextLineEnd, pTmpLine = *pCurLine;
int moveSize;
BOOL newBlockCreated = FALSE;
int curLineOffset;
//Check not to move first line of current block in next block
if ((LPSTR)*pCurLine - (LPSTR)(*pCurBlock)->Data == 0) {
pTmpLine = (LPLINEREC)((LPSTR) pTmpLine + pTmpLine->Length);
}
moveSize = (int) (pBlockEnd - (LPSTR)pTmpLine);
if ((*pCurBlock)->NextBlock == NULL) {
//Create a new block
if (!AllocateBlock(*pCurBlock, NULL, &pNextBlock)) {
return FALSE;
}
newBlockCreated = TRUE;
Docs[doc].LastBlock = pNextBlock;
pNextLineEnd = (LPLINEREC)pNextBlock->Data;
}
else {
//Before allocating a new block, look into next block
//to see if we have space to insert chars and to move in
//current line and lines after
pNextBlock = (*pCurBlock)->NextBlock;
pNextLineEnd = (LPLINEREC)(pNextBlock->Data
+ pNextBlock->LastLineOffset);
pNextLineEnd = (LPLINEREC)((LPSTR)pNextLineEnd
+ pNextLineEnd->Length);
if (moveSize + sizeExpansion > (int)(BLOCK_SIZE
- ((LPSTR)pNextLineEnd - (LPSTR)pNextBlock->Data))) {
LPBLOCKDEF pNewBlock;
//We have not enough space in next block, so insert
//a new created block
if (!AllocateBlock(*pCurBlock, pNextBlock, &pNewBlock)) {
return FALSE;
}
newBlockCreated = TRUE;
pNextBlock->PrevBlock = pNewBlock;
//Next block is new block
pNextBlock = pNewBlock;
pNextLineEnd = (LPLINEREC)pNextBlock->Data;
}
}
curLineOffset = (int) ((LPSTR)pTmpLine - (LPSTR)(*pCurBlock)->Data);
//Link current block to next
(*pCurBlock)->NextBlock = pNextBlock;
//Shift right next blocks chars
memmove(pNextBlock->Data + moveSize,
pNextBlock->Data,
(size_t) ((LPSTR)pNextLineEnd - (LPSTR)pNextBlock->Data));
//And copy chars from current block to next block
memmove(pNextBlock->Data, (LPSTR)pTmpLine, moveSize);
Assert(moveSize >= LHD);
//Rework pointers
if (newBlockCreated) {
pNextBlock->LastLineOffset = (*pCurBlock)->LastLineOffset -
curLineOffset;
} else {
pNextBlock->LastLineOffset += moveSize;
}
(*pCurBlock)->LastLineOffset = curLineOffset - pTmpLine->PrevLength;
//Current block is next block if current line is not block first line
if ((LPSTR)*pCurLine != (LPSTR)(*pCurBlock)->Data) {
//Cur line is now next block first line
*pCurLine = (LPLINEREC)pNextBlock->Data;
//Next block becomes current block
*pCurBlock = pNextBlock;
}
return TRUE;
} /* FindInsertSpace() */
//When your file has an Undo buffer, be sure to set the undo/redo
//engine to UNDO before inserting chars, otherwise they will be stored
//in the REDO buffer
BOOL FAR PASCAL
InsertBlock(
int doc,
int col,
long line,
int size,
LPSTR buf
)
{
LPLINEREC pCurLine = NULL, pLastLine = NULL, pNextLine = NULL;
LPBLOCKDEF pCurBlock, pNextBlock;
LPSTR charsBegin = NULL;
LPSTR charsEnd = NULL;
LPSTR pBlockEnd = NULL;
LPSTR start = NULL;
int sizeExpansion, newLineLen, charsSize;
int extraBlanks, lineLen;
int nextLineLength;
int freeBlockSize;
BOOL addOneLine;
LPDOCREC d = &Docs[doc];
UNDOREDOREC pUndoRedo;
int startLine = line;
BOOL firstLine = TRUE;
int nbLines = d->NbLines;
char lineBuf[MAX_LINE_SIZE];
int expandedLen;
//Beep and exit if file is ReadOnly
if (d->readOnly || ROREGION(d,col,line)) {
ReadOnlyBeep();
return FALSE;
} else {
// Clear error status
QueryReadOnlyError();
}
if (size == 0) {
return TRUE;
}
editorIsCritical = TRUE;
Assert(line < d->NbLines);
col = min(col, MAX_USER_LINE);
d->lineTop = d->lineBottom = line;
//Get information from line where we insert
if (!FirstLine(doc, &pCurLine, &line, &pCurBlock)) {
goto errRet;
}
//Align column respecting tabs
if (playingRecords) {
expandedLen = ConvertPosX(col);
} else {
expandedLen = col;
col = AlignToTabs(expandedLen, pCurLine->Length - LHD,
(LPSTR)pCurLine->Text);
}
Assert(col >= 0);
//Open an edit record
if (!OpenRec(doc, DELETESTREAM, col, line - 1)) {
goto errRet;
}
//Initialize rec buffer ptr if we will read chars from undo/redo record
if (buf) {
charsBegin = charsEnd = buf;
} else if (d->recType == REC_UNDO) {
pUndoRedo = d->redo;
} else if (d->recType == REC_REDO) {
pUndoRedo = d->undo;
} else {
Assert(FALSE);
return FALSE;
}
//Insert until buffer empty
while (size) {
if (buf != NULL) {
//Extract chars from supplied buf until EOF, EOL or end of buffer
while (size && *charsEnd != CR) {
if (*charsEnd == TAB) {
expandedLen += g_contGlobalPreferences_WkSp.m_nTabSize - (expandedLen % g_contGlobalPreferences_WkSp.m_nTabSize);
} else {
expandedLen++;
}
charsEnd++;
size--;
}
} else {
//Extract from undo/redo records buffer a line, or the text if
//no CR+LF found (no overflow protection needed, by definition
//lines in undo/redo buf are never beyond maximum)
charsBegin = (LPSTR)lineBuf;
ReadLineFromBuf(&pUndoRedo, lineBuf, &size, &expandedLen,&charsEnd);
}
if (size == 0) {
addOneLine = FALSE;
} else if ( (addOneLine = (*charsEnd == CR)) &&
d->NbLines >= MAX_LINE_NUMBER) {
ErrorBox(ERR_Too_Many_Lines);
goto errRet;
}
lineLen = pCurLine->Length - LHD;
//We are in a line, check if we are after last char
extraBlanks = max (col - lineLen, 0);
//Compute new line len whether chars end with a CR or not
newLineLen =
sizeExpansion =
charsSize = (int) (charsEnd - charsBegin);
if (addOneLine) {
newLineLen += col + LHD;
sizeExpansion += LHD;
//Adjust startline for breakpoints and error handling
if (firstLine && col > 0) {
startLine++;
}
} else {
int newCol = col;
newLineLen += (pCurLine->Length + extraBlanks);
//Now see if what we insert in current line will not expand
//line len beyond maximum
if (extraBlanks == 0) {
while (newCol < lineLen) {
if (pCurLine->Text[newCol] == TAB) {
expandedLen += g_contGlobalPreferences_WkSp.m_nTabSize - (expandedLen % g_contGlobalPreferences_WkSp.m_nTabSize);
} else {
expandedLen++;
}
newCol++;
}
}
}
//Check new line length
if (expandedLen > MAX_USER_LINE) {
ErrorBox(ERR_Line_Too_Long);
goto errRet;
}
firstLine = FALSE;
//Possibly add extraBlanks to buffer size expansion
sizeExpansion += extraBlanks;
//Sorry but recursivity eats stack
reDoIt:
//Keep a pointer on last line of block
pLastLine = (LPLINEREC)((LPSTR)pCurBlock->Data +
pCurBlock->LastLineOffset);
//Check block size
freeBlockSize = BLOCK_SIZE - pCurBlock->LastLineOffset -
pLastLine->Length;
//Compute very end of block
pBlockEnd = (LPSTR)pLastLine + pLastLine->Length;
Assert(pBlockEnd <= (LPSTR)(pCurBlock)->Data + BLOCK_SIZE);
//Do we have room in current block
if (freeBlockSize >= sizeExpansion) {
//We have space to insert chars in current block
//If we add a line, first update next line length
if (addOneLine) {
nextLineLength = pCurLine->Length - (BYTE)newLineLen
+ (BYTE)sizeExpansion;
pNextLine = (LPLINEREC)((LPSTR)pCurLine + pCurLine->Length);
//Write future line length in next line prev length field,
//if line is the last of block, load next block
if (((LPSTR)pNextLine - (LPSTR)pCurBlock->Data)
<= pCurBlock->LastLineOffset) {
pNextLine->PrevLength = (BYTE)nextLineLength;
} else {
//Write header only if there is a line after
pNextBlock = pCurBlock->NextBlock;
if (pNextBlock != NULL) {
//Set next line pointer in first line of Next Block
pNextLine = (LPLINEREC)pNextBlock->Data;
pNextLine->PrevLength = (BYTE)nextLineLength;
}
}
}
//Compute the position where to insert
start = (LPSTR)(pCurLine) + min(col + LHD, (int)pCurLine->Length);
//Shift right part
memmove(start + sizeExpansion, start, (size_t) (pBlockEnd - start));
//First insert the extraBlanks if any
memset(start, ' ', extraBlanks);
start += extraBlanks;
//Then insert chars
memmove(start, charsBegin, charsSize);
//Possibly adjust block last Line Offset
// - We inserted before last line, propagate line extension
// - We inserted at last line, propagate line extension
// only if a new line is inserted
if (pCurLine < pLastLine) {
pCurBlock->LastLineOffset +=sizeExpansion;
} else if (pCurLine == pLastLine) {
if (addOneLine) {
pCurBlock->LastLineOffset +=newLineLen;
}
} else {
Assert(FALSE);
return FALSE;
}
pNextLine = (LPLINEREC)((LPSTR)pCurLine + newLineLen);
Assert((LPSTR)pNextLine - (LPSTR)pCurBlock->Data <= BLOCK_SIZE);
//Write next line header, in block or in next block
if (((LPSTR)pCurLine - (LPSTR)pCurBlock->Data)
== pCurBlock->LastLineOffset) {
//Write header only if there is a line after
pNextBlock = pCurBlock->NextBlock;
if (pNextBlock != NULL) {
//Set next line pointer to first line of Next Block
pNextLine = (LPLINEREC)pNextBlock->Data;
pNextLine->PrevLength = (BYTE)newLineLen;
}
} else
//Not at last line of block, Set next line previous line
pNextLine->PrevLength = (BYTE)newLineLen;
//If we insert a new line : Change next line length and
//set current line status to 0 if it's a brand new line
if (addOneLine) {
pNextLine->Length = (BYTE)nextLineLength;
if (col == 0) {
//Propagate status to next line
pNextLine->Status = pCurLine->Status;
pCurLine->Status = 0;
} else {
//Set next line status to 0
AssertAligned(pNextLine);
pNextLine->Status = 0;
}
}
//Set cur line length
pCurLine->Length = (BYTE)newLineLen;
} else {
if (!FindInsertSpace(doc, &pCurBlock, &pCurLine, pBlockEnd,
sizeExpansion)) {
goto errRet;
}
//Insert chars in next block
goto reDoIt;
}
if (addOneLine) {
charsEnd += 2;
size -=2;
d->lineBottom++;
d->NbLines++;
expandedLen = 0;
}
charsBegin = charsEnd;
col = 0;
if (size || addOneLine) {
pCurLine = pNextLine;
}
}
//Close undo/redo record
CloseRec(doc, max(0, (int)((start - (LPSTR)pCurLine->Text) + charsSize)),
d->lineBottom, TRUE);
//Inform breakpoints and errors handlers of what we did in the text
//(only if it's a real insertion)
if (d->recType != REC_STOPPED && d->NbLines > nbLines) {
AdjustDebugLines(doc, startLine, d->NbLines - nbLines, TRUE);
}
CloseLine(doc, &pCurLine, d->lineBottom + 1, &pCurBlock);
//Document is modified
ModifiedDoc(doc);
//If there was nothing to record, tells the system that now we have
//something in buffer
if (d->playCount == REC_CANNOTUNDO) {
d->playCount = 0;
}
if (d->language != NO_LANGUAGE) {
CheckSyntax(doc);
}
editorIsCritical = FALSE;
return TRUE;
errRet: {
//Cancel recording
CloseRec(doc, 0, 0, FALSE);
//Try to resynchronize system on first line of document
pCurLine = (LPLINEREC)d->FirstBlock->Data;
CloseLine(doc, &pCurLine, 1, &d->FirstBlock);
editorIsCritical = FALSE;
return FALSE;
}
} /* InsertBlock() */
BOOL NEAR PASCAL
CancelDelete(
int doc
)
{
if (QuestionBox(ERR_UndoRedoBufferTooSmall, MB_YESNO) == IDYES) {
//Cancel this record and stop recording for this action
CloseRec(doc, 0, 0, FALSE);
Docs[doc].recType = REC_HADOVERFLOW;
return FALSE;
} else {
return TRUE;
}
}
//Delete characters on a single line, pointers are unaffected
BOOL NEAR PASCAL
PartialLineDelete(
int doc,
LPBLOCKDEF *pCurBlock,
LPLINEREC *pCurLine,
int * totalSize,
int col1,
int col2,
BOOL moveToNextLine
)
{
int moveSize;
LPLINEREC pNextLine = NULL, pLastLine;
LPBLOCKDEF pNextBlock = *pCurBlock;
//Adjust col1 and col2 inside the line
col1 = min(col1, (*pCurLine)->Length - LHD);
col2 = min(col2, (*pCurLine)->Length - LHD);
moveSize = col2 - col1;
//Send deleted chars to record buffer and roll back
//if buffer full
if (!AppendToRec(doc, (*pCurLine)->Text + col1, moveSize, FALSE, totalSize))
if (CancelDelete(doc))
return FALSE;
pLastLine = (LPLINEREC)((*pCurBlock)->Data + (*pCurBlock)->LastLineOffset);
//Move chars
if (moveSize > 0) {
memmove((LPSTR)(*pCurLine)->Text + col1,
(LPSTR)(*pCurLine)->Text + col2,
(size_t) ((LPSTR)pLastLine + pLastLine->Length
- ((LPSTR)(*pCurLine)->Text + col2)));
}
//Update current line length
(*pCurLine)->Length -= (BYTE) moveSize;
//Update last line offset and next line previous length (in current
//or in next block)
if ((*pCurLine) == pLastLine) {
//Next line is in next block
pNextBlock = (*pCurBlock)->NextBlock;
if (pNextBlock != NULL) {
//Set next line pointer to first line of Next Block and
//update previous length
pNextLine = (LPLINEREC)pNextBlock->Data;
pNextLine->PrevLength -= (BYTE) moveSize;
}
}
else {
(*pCurBlock)->LastLineOffset -= moveSize;
pNextLine = (LPLINEREC)((LPSTR)*pCurLine + (*pCurLine)->Length);
pNextLine->PrevLength -= (BYTE) moveSize;
}
if (moveToNextLine) {
*pCurLine = pNextLine;
*pCurBlock = pNextBlock;
}
return TRUE;
}
//Deletes the pCurLine in the pCurBlock and adjust them to the
//next line, if the line was the last line of the block, the block
//is deleted
BOOL NEAR PASCAL
FullLineDelete(
int doc,
LPBLOCKDEF *pCurBlock,
LPLINEREC *pCurLine,
int *totalSize
)
{
LPLINEREC pLastLine;
BYTE prevLineLen = (*pCurLine)->PrevLength;
//First send deleted chars to record buffer
if (!AppendToRec(doc, (*pCurLine)->Text, (*pCurLine)->Length - LHD, TRUE,
totalSize)) {
if (CancelDelete(doc)) {
return FALSE;
}
}
//Compute lastLine and nextLine in block
pLastLine = (LPLINEREC)((*pCurBlock)->Data + (*pCurBlock)->LastLineOffset);
if (*pCurLine == pLastLine) {
//If line is also the first line of block, delete the block
if (*pCurLine == (LPLINEREC)(*pCurBlock)->Data) {
Assert((*pCurBlock)->LastLineOffset == 0);
//Document may be empty, reinitialize first block
if ((*pCurBlock)->PrevBlock == NULL &&
(*pCurBlock)->NextBlock == NULL) {
(*pCurLine)->Length = LHD;
(*pCurLine)->Status = 0;
(*pCurBlock)->LastLineOffset = 0;
Docs[doc].NbLines = 1;
Assert((*pCurLine)->PrevLength == 0);
Assert(Docs[doc].FirstBlock == *pCurBlock);
Assert(Docs[doc].LastBlock == *pCurBlock);
return TRUE;
} else {
//Delete block, and set current block to next block
*pCurBlock = FreeBlock(doc, *pCurBlock);
}
} else {
//Adjust the current block last offset
(*pCurBlock)->LastLineOffset -= prevLineLen;
Assert((*pCurBlock)->LastLineOffset >= 0);
//Set curBlock to next block
*pCurBlock = (*pCurBlock)->NextBlock;
}
//Set next line to first line of block
if (*pCurBlock != NULL) {
*pCurLine = (LPLINEREC)(*pCurBlock)->Data;
}
} else {
LPLINEREC pNextLine = (LPLINEREC)((LPSTR)*pCurLine +
(*pCurLine)->Length);
//Adjust block last offset
(*pCurBlock)->LastLineOffset -= (*pCurLine)->Length;
Assert((*pCurBlock)->LastLineOffset >= 0);
//Now shift left rest of block chars
memmove(*pCurLine,
(LPSTR)pNextLine,
(size_t) ((LPSTR)pLastLine + pLastLine->Length - (LPSTR)pNextLine));
}
//If next line exist, change it's header, otherwise resynchronise
if (*pCurBlock != NULL) {
(*pCurLine)->PrevLength = prevLineLen;
} else {
*pCurBlock = Docs[doc].LastBlock;
*pCurLine = (LPLINEREC)((*pCurBlock)->Data +
(*pCurBlock)->LastLineOffset);
}
Docs[doc].NbLines--;
return TRUE;
}
//Join the current line to the preceding, pad blanks if necessary
BOOL NEAR PASCAL
JoinLines(
int doc,
LPBLOCKDEF pCurBlock,
LPLINEREC pCurLine,
int *totalSize,
int lineNb,
int col1
)
{
LPDOCREC d = &Docs[doc];
WORD undoRedoState = d->recType;
int limit;
LPLINEREC pPrevLine;
char tmpLine[MAX_USER_LINE + 2]; //2 = Possible CR+LF
WORD status;
BOOL language = d->language;
//First compose the resulting new line
if (pCurLine == (LPLINEREC)pCurBlock->Data) {
pCurBlock = pCurBlock->PrevBlock;
Assert(pCurBlock != NULL);
pPrevLine = (LPLINEREC)((LPSTR)pCurBlock->Data +
pCurBlock->LastLineOffset);
} else {
pPrevLine = (LPLINEREC)((LPSTR)pCurLine - pCurLine->PrevLength);
}
//Save portion of first line and save status (unless all first line
//is deleted)
if (col1 >0) {
status = pPrevLine->Status;
} else {
status = 0;
}
limit = min(col1, pPrevLine->Length - LHD);
memmove((LPSTR)tmpLine, (LPSTR)pPrevLine->Text, limit);
//We may have now to pad line with blanks
for(; limit < MAX_USER_LINE && limit < col1; limit++) {
tmpLine[limit] = ' ';
}
//Append now current line
if (limit + pCurLine->Length - LHD <= MAX_USER_LINE) {
memmove((LPSTR)tmpLine + limit, (LPSTR)pCurLine->Text,
pCurLine->Length - LHD);
limit += pCurLine->Length - LHD;
}
else {
ErrorBox(ERR_Line_Too_Long);
return FALSE;
}
//Check line overflow
if (limit >= MAX_USER_LINE) {
ErrorBox(ERR_Line_Too_Long);
return FALSE;
}
//Append a CR+LF
memmove(tmpLine + limit, CrLf, 2);
limit += 2;
//Insert the new one
CloseLine(doc, &pPrevLine, lineNb--, &pCurBlock);
//Stop Recording
d->recType = REC_STOPPED;
d->language = NO_LANGUAGE; //To avoid syntax check
if (!InsertBlock(doc, 0, lineNb++, limit, (LPSTR)tmpLine)) {
return FALSE;
}
d->language = (WORD) language;
//Resynchronize to next line
pCurBlock = d->CurrentBlock;
pCurLine = (LPLINEREC)((LPSTR)pCurBlock->Data + d->CurrentLineOffset);
pCurLine->Status = 0;
//Write saved status in previous line (the one just inserted)
if (pCurLine == (LPLINEREC)pCurBlock->Data) {
LPBLOCKDEF pPrevBlock = pCurBlock->PrevBlock;
if (pPrevBlock != NULL) {
((LPLINEREC)((LPSTR)pPrevBlock->Data + pPrevBlock->LastLineOffset)) ->Status = status;
}
} else {
((LPLINEREC)((LPSTR)pCurLine - pCurLine->PrevLength))->Status = status;
}
//Delete now the 2 lines
if (!FullLineDelete(doc, &pCurBlock, &pCurLine, totalSize)) {
return FALSE;
}
if (!FullLineDelete(doc, &pCurBlock, &pCurLine, totalSize)) {
return FALSE;
}
d->recType = undoRedoState;
CloseLine(doc, &pCurLine, min(d->NbLines, lineNb + 1), &pCurBlock);
return TRUE;
}
//When your file has an Undo buffer, be sure to set the undo/redo
//engine to UNDO before deleting chars, otherwise they will be stored
//in the REDO buffer
BOOL FAR PASCAL
DeleteBlock(
int doc,
int col1,
long line1,
int col2,
long line2
)
{
LPLINEREC pCurLine;
LPBLOCKDEF pCurBlock;
int curLine = line1, startLine = line1;
int editCol1, editCol2;
LPDOCREC d = &Docs[doc];
int totalSize = 0;
WORD undoRedoState;
int totalNbLines = d->NbLines;
//Beep and exit if file is ReadOnly
if (d->readOnly || ROREGION(d,col1,line1)) {
ReadOnlyBeep();
return FALSE;
} else {
// Clear error status
QueryReadOnlyError();
}
//Verifications and adjustements
Assert(line1 < totalNbLines);
Assert(line2 <= totalNbLines);
Assert(line2 >= line1);
editCol1 = min(col1, MAX_USER_LINE);
editCol2 = min(col2, MAX_USER_LINE + 1);
d->lineTop = d->lineBottom = line1;
editorIsCritical = TRUE;
//Get information about line we start delete
if (!FirstLine (doc, &pCurLine, &line1, &pCurBlock)) {
//Try to resynchronize system on first line of document
pCurLine = (LPLINEREC)(d->FirstBlock->Data);
CloseLine(doc, &pCurLine, 1, &d->FirstBlock);
ClearSelection(curView);
editorIsCritical = FALSE;
return FALSE;
}
line1--;
//Remember record action
undoRedoState = d->recType;
//Align column respecting tabs
if (playingRecords) {
col1 = editCol1;
} else {
col1 = AlignToTabs(editCol1, pCurLine->Length - LHD, (LPSTR)pCurLine->Text);
}
//Open an edit record, if buffer full, ask the user for action
if (!OpenRec(doc, INSERTSTREAM, col1, curLine)) {
if (CancelDelete(doc)) {
goto rollBack;
}
}
//Full line(s) delete
if (col1 == 0 && col2 == 0) {
for (curLine = line1; curLine < line2; curLine++) {
if (!FullLineDelete(doc, &pCurBlock, &pCurLine, &totalSize)) {
goto rollBack;
}
}
CloseLine(doc, &pCurLine, line1+1, &pCurBlock);
} else if (line1 == line2) {
if (playingRecords) {
col2 = editCol2;
} else {
col2 = AlignToTabs(editCol2, pCurLine->Length - LHD, (LPSTR)pCurLine->Text);
}
//The 2 columns could be in the same tab
if (col1 == col2) {
col2++;
}
if (!PartialLineDelete(doc, &pCurBlock, &pCurLine, &totalSize, col1, col2, FALSE)) {
goto rollBack;
}
CloseLine(doc, &pCurLine, line1+1, &pCurBlock);
} else {
//Tricky case, deletion spreads over several lines
Assert(curLine < line2);
//Delete the right part of first line
if (!PartialLineDelete(doc,
&pCurBlock,
&pCurLine,
&totalSize,
col1,
pCurLine->Length - LHD,
TRUE)) {
goto rollBack;
}
//Insert a fake CrLf in Undo buffer,
if (!AppendToRec(doc, (LPSTR)NULL, 0, TRUE, &totalSize)) {
if (CancelDelete(doc)) {
goto rollBack;
}
}
//Delete lines in between
for (curLine = line1+1; curLine < line2; curLine++) {
if (!FullLineDelete(doc, &pCurBlock, &pCurLine, &totalSize)) {
goto rollBack;
}
}
if (playingRecords) {
col2 = editCol2;
} else {
col2 = AlignToTabs(editCol2, pCurLine->Length - LHD, (LPSTR)pCurLine->Text);
}
//Delete the left part of last line
if (!PartialLineDelete(doc,
&pCurBlock,
&pCurLine,
&totalSize,
0,
col2,
FALSE)) {
goto rollBack;
}
//Rebuild resulting line
if (!JoinLines(doc, pCurBlock, pCurLine, &totalSize, line1+1, col1)) {
goto rollBack;
}
//Inform breakpoints and errors handler
if (col1 > 0) {
startLine++;
}
}
//Document is modified
ModifiedDoc(doc);
//Inform breakpoints handler of what we did in the text
if (d->NbLines < totalNbLines) {
AdjustDebugLines(doc, startLine, totalNbLines - d->NbLines, FALSE);
}
//If the recording was canceled, this close will do nothing
CloseRec(doc, 0, 0, TRUE);
//If there was nothing to record, tells the system that now we have
//something in buffer, if we had an overflow, restart Undo/Redo
if (d->recType != REC_HADOVERFLOW) {
d->recType = undoRedoState;
} else {
d->playCount = REC_CANNOTUNDO;
}
if (d->language != NO_LANGUAGE) {
CheckSyntax(doc);
}
editorIsCritical = FALSE;
return TRUE;
//Record buffer full
rollBack: {
CloseRec(doc, 0, 0, TRUE);
//Document is modified
ModifiedDoc(doc);
//Try to resynchronize system on first line of document
pCurBlock = d->FirstBlock;
pCurLine = (LPLINEREC)pCurBlock->Data;
CloseLine(doc, &pCurLine, 1, &pCurBlock);
ClearSelection(curView);
editorIsCritical = FALSE;
//Play the recorded information
PlayRec(doc, d->recType, FALSE, FALSE);
return FALSE;
}
}
BOOL FAR PASCAL
DeleteAll(
int doc
)
{
return DeleteBlock(doc, 0, 0, MAX_USER_LINE + 1, Docs[doc].NbLines - 1);
}
BOOL FAR PASCAL
ReplaceCharInBlock(
int doc,
int x,
long y,
int ch
)
{
LPLINEREC pCurLine;
LPBLOCKDEF pCurBlock;
LPDOCREC d = &Docs[doc];
int realX;
//Beep and exit if file is ReadOnly
if (d->readOnly || ROREGION(d,x,y)) {
ReadOnlyBeep();
return TRUE;
} else {
// Clear error status
QueryReadOnlyError();
}
//Get information from line we replace char
if (!FirstLine (doc, &pCurLine, &y, &pCurBlock))
return FALSE;
CloseLine(doc, &pCurLine, y, &pCurBlock);
if (playingRecords) {
realX = x;
} else {
realX = AlignToTabs(x, pCurLine->Length - LHD, (LPSTR)pCurLine->Text);
}
if (realX >= pCurLine->Length - LHD) {
//Character position is after line last char, insert char
return InsertBlock(doc, x, y - 1, 1, (LPSTR)&ch);
} else {
d->lineTop = d->lineBottom = y - 1;
//It's a real replace, open an edit record, append char and
//close record
if (!OpenRec(doc, REPLACECHAR, realX, y - 1)) {
return FALSE;
}
if (!AppendToRec(doc, (LPSTR)pCurLine->Text + realX, 1, FALSE, NULL)) {
return FALSE;
}
CloseRec(doc, 0, 0, TRUE);
//Character is inside line, just replace it
pCurLine->Text[realX] = (char)ch;
//Document is modified
ModifiedDoc(doc);
if (d->language != NO_LANGUAGE) {
CheckSyntax(doc);
}
return TRUE;
}
}
ULONG FAR PASCAL CountCharsInDocument(
int doc)
{
LPLINEREC pl;
LPBLOCKDEF pb;
long y;
ULONG nbChars;
y = 0;
//Load first line of document
if (!FirstLine (doc, &pl, &y, &pb)) {
Assert(FALSE);
return 0L;
}
//Count chars in first line
nbChars = pl->Length - LHD + 2; //2 = CR+LF
//Count chars in middle lines
while (y < Docs[doc].NbLines) {
//Load next line of document
if (!NextLine (doc, &pl, &y, &pb)) {
Assert(FALSE);
return 0;
}
//Count chars in current line
nbChars += pl->Length - LHD + 2; //2 = CR+LF
}
//Refresh pointers
CloseLine (doc, &pl, y, &pb);
return nbChars;
}
//Scan all document and try to compress data by moving lines and
//filling blocks
BOOL FAR PASCAL
CompactDocument(
int doc
)
{
LPLINEREC pCurLine, pNextLine;
LPBLOCKDEF pCurBlock, pNextBlock;
int freeBlockSize;
BOOL more;
LPDOCREC d = &Docs[doc];
long spaceRecovered = 0;
//Load last line of first block
pCurBlock = d->FirstBlock;
pNextBlock = pCurBlock->NextBlock;
while (pNextBlock != NULL) {
pNextLine = (LPLINEREC)(pNextBlock->Data);
more = TRUE;
while (more) {
pCurLine = (LPLINEREC)(pCurBlock->Data + pCurBlock->LastLineOffset);
freeBlockSize = BLOCK_SIZE - pCurBlock->LastLineOffset -
pCurLine->Length;
//Have we space to move next line in current block ?
if (freeBlockSize < (int)pNextLine->Length) {
more = FALSE;
} else {
//Move first line from next block in current block
pCurBlock->LastLineOffset += pCurLine->Length;
memmove((LPSTR)pCurLine + pCurLine->Length,
pNextLine,
pNextLine->Length);
//Remove line from next block
pNextBlock->LastLineOffset -= pNextLine->Length;
spaceRecovered += pNextLine->Length;
memmove((LPSTR)pNextBlock->Data,
(LPSTR)pNextLine + pNextLine->Length,
BLOCK_SIZE - (size_t) (((LPSTR)pNextLine + pNextLine->Length -
(LPSTR)pNextBlock->Data)));
//Free next block if empty
if (pNextBlock->LastLineOffset < 0) {
pNextBlock = FreeBlock(doc, pNextBlock);
if (pNextBlock == NULL) {
more = FALSE;
} else {
pNextLine = (LPLINEREC)(pNextBlock->Data);
}
}
}
}
//Next block becomes current block
pCurBlock = pCurBlock->NextBlock;
if (pCurBlock != NULL) {
pNextBlock = pCurBlock->NextBlock;
}
}
//Resynchronize to first line of document
d->CurrentLine = 0;
d->CurrentBlock = d->FirstBlock;
d->CurrentLineOffset = 0;
return TRUE;
}
BOOL FAR PASCAL
CheckDocument(
int doc
)
{
LPLINEREC pl;
LPBLOCKDEF pb, previousBlock;
BOOL curBlockFound = FALSE, lastBlockFound = FALSE;
BOOL curLineFound = FALSE;
BYTE curLen;
int nbLines = 0;
LPDOCREC d = &Docs[doc];
int nbBl = 0;
DWORD dataSize = 0;
DWORD totalSize;
pb = d->FirstBlock;
Assert(pb != NULL);
previousBlock = NULL;
curLen = 0;
while (pb != NULL) {
if (pb == d->CurrentBlock) {
curBlockFound = TRUE;
}
if (pb == d->LastBlock) {
lastBlockFound = TRUE;
}
if (pb->PrevBlock != previousBlock) {
return AuxPrintf(1, "ChkDoc:Prev block pointer (Line %i)", nbLines);
}
if (pb->LastLineOffset > BLOCK_SIZE - LHD) {
return AuxPrintf(1, "ChkDoc:Line last offset above (Line %i)",
nbLines);
}
if (pb->LastLineOffset < 0) {
return AuxPrintf(1, "ChkDoc:Line last offset below (Line %i)",
nbLines);
}
pl = (LPLINEREC)(pb)->Data;
while (pl <= (LPLINEREC)(pb->Data + pb->LastLineOffset)) {
if (d->CurrentBlock == pb) {
if (d->CurrentLineOffset == ((LPSTR)pl - (LPSTR)pb->Data)) {
curLineFound = TRUE;
if (nbLines != d->CurrentLine) {
return AuxPrintf(1, "ChkDoc:Bad current line (Line %i)",
nbLines);
}
}
}
if (pl->PrevLength != curLen) {
return AuxPrintf(1, "ChkDoc:Bad prev length (Line %i)",
nbLines);
}
curLen = pl->Length;
dataSize += curLen;
if (curLen < LHD) {
return AuxPrintf(1, "ChkDoc:Bad length (Line %i)", nbLines);
}
if ((LPSTR)pl + curLen > (LPSTR)pb->Data + BLOCK_SIZE) {
return AuxPrintf(1, "ChkDoc:Line above block (Line %i)",
nbLines);
}
pl = (LPLINEREC) ((LPSTR)pl + curLen);
nbLines++;
}
previousBlock = pb;
pb = pb->NextBlock;
if (pb == NULL) {
if (d->LastBlock != previousBlock) {
return AuxPrintf(1, "ChkDoc: Bad last block (Line %i)",
nbLines);
}
}
nbBl++;
}
totalSize = (DWORD)nbBl * sizeof(BLOCKDEF);
Assert(totalSize);
AuxPrintf(1,
"Blocks = %i, Total Size = %lu, Data Size = %lu, Efficiency = %lu\\%",
nbBl, totalSize, dataSize, (100 * dataSize) / totalSize);
if (!curBlockFound) {
return AuxPrintf(1, "ChkDoc:Lost cur block (Line %i)", nbLines);
}
if (!lastBlockFound) {
return AuxPrintf(1, "ChkDoc:Lost last block (Line %i)", nbLines);
}
if (!curLineFound){
return AuxPrintf(1, "ChkDoc:Lost cur line (Line %i)", nbLines);
}
if (nbLines != d->NbLines) {
return AuxPrintf(1,
"ChkDoc:Wrong total of lines (Doc = %i, Count = %i)",
d->NbLines, nbLines);
}
return TRUE;
}
void FAR PASCAL
DumpDocument(
int doc
)
{
LPLINEREC pl;
LPBLOCKDEF pb;
int line = 0;
char s[10], t[10];
LPDOCREC d = &Docs[doc];
int i;
pb = d->FirstBlock;
while (pb != NULL) {
_itoa(HIWORD((DWORD_PTR)pb), t, 16);
while ((int)strlen(t) <= 4) {
strcat(t, " ");
}
strcat(t, ":");
pl = (LPLINEREC)(pb)->Data;
while (pl <= (LPLINEREC)(pb->Data + pb->LastLineOffset)) {
s[0]='\0';
if (d->CurrentBlock == pb) {
if (d->CurrentLineOffset == ((LPSTR)pl - (LPSTR)pb->Data)) {
_itoa((int)d->CurrentLine, s, 10);
}
}
while ((int)strlen(s) <= 2) {
strcat(s, " ");
}
strncpy((LPSTR)szTmp, pl->Text, pl->Length - LHD);
for (i = 0; i < (int)strlen(szTmp); i++) {
if(IsDBCSLeadByte((BYTE)szTmp[i])) {
i++;
}
if (szTmp[i] == TAB) {
szTmp[i] = 126;
}
}
szTmp[min(200, pl->Length - LHD)] = '\0';
AuxPrintf(1, "%s%3u [%3i,%3i,%3i] %s '%s'",
(LPSTR)t, line++, pl->Status, pl->PrevLength,
pl->Length, (LPSTR)s, (LPSTR)szTmp);
strcpy(t," :");
pl = (LPLINEREC) ((LPSTR)pl + pl->Length);
}
pb = pb->NextBlock;
}
AuxPrintf(1, "");
}
BOOL FAR PASCAL
DestroyDocument(
int doc
)
{
LPBLOCKDEF pb;
LPDOCREC d = &Docs[doc];
//Destroy undo and possibly redo buffers
DestroyRecBuf(doc, REC_UNDO);
if (d->playCount != 0 && d->playCount != REC_CANNOTUNDO) {
DestroyRecBuf(doc, REC_REDO);
}
//And blocks of memory allocated for it
while (d->FirstBlock != NULL) {
pb = d->FirstBlock;
Assert(pb->NextBlock != NULL || pb == d->LastBlock);
d->FirstBlock = pb->NextBlock;
if (!DocFree((LPSTR)pb)) {
InternalErrorBox(SYS_Free_Memory);
}
}
return TRUE;
}
//Retrieve chars on a line
BOOL FAR PASCAL
GetTextAtLine(
int doc,
long line,
int col1,
int col2,
LPSTR text
)
{
LPLINEREC pl;
LPBLOCKDEF pb;
if (!FirstLine (doc, &pl, &line, &pb))
return FALSE;
CloseLine(doc, &pl, line, &pb);
col2 = min(col2, elLen);
col1 = min(col1, col2);
memmove(text, el + col1, col2 - col1);
text[col2 - col1] = '\0';
return TRUE;
}