home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 5 Edit
/
05-Edit.zip
/
NOTEPAD2.ZIP
/
MPIECE.C
< prev
next >
Wrap
C/C++ Source or Header
|
1989-02-08
|
29KB
|
980 lines
/*
* mpiece.c -- Piece management
*
* Created by Microsoft Corporation, 1989
*/
#include <os2.h>
#include "mtypes.h"
#include "mfuncs.h"
/***********************************************************************
* Internal forward declarations
***********************************************************************/
private PPR CreatePiecerec(PED ped, OFFSET cch, PCHAR pch, PSEGDESC psd);
private PSEGDESC CreateSegment(PED ped, OFFSET cchSize);
private VOID BubbleUp(PED ped, PSEGDESC psd);
private VOID DeleteTextOrder(PPR ppr);
/***********************************************************************
* Piece record manipulation
***********************************************************************/
/* private function
*
* PPR CreatePiecerec(PED ped, OFFSET cch, PCHAR pch, PSEGDESC psd)
*
* Creates a new empty piece record for cch characters at address
* pch. Freespace count in segment must be maintained elsewhere.
*/
private PPR CreatePiecerec(PED ped, OFFSET cch, PCHAR pch, PSEGDESC psd)
{
PPR ppr;
ppr = AllocStore(ped, sizeof(PIECE));
if (ppr == NULL) {
return(ppr);
}
ppr->pprTextNext = NULL;
ppr->pprTextPrev = NULL;
ppr->pprPhysNext = NULL;
ppr->pprPhysPrev = NULL;
ppr->pchText = pch;
ppr->cchText = 0;
ppr->cchUnused = cch;
ppr->psd = psd;
return(ppr);
}
/* private function
*
* PMR CreateMarkerPiece(PED ped)
*
* Creates a new marker piece record. Returns NULL on failure.
*/
private PMR CreateMarkerPiece(PED ped)
{
PMR pmr;
pmr = AllocStore(ped, sizeof(MARKERPIECE));
if (pmr != NULL) {
pmr->pr.pprTextNext = NULL;
pmr->pr.pprTextPrev = NULL;
pmr->pr.pprPhysNext = NULL;
pmr->pr.pprPhysPrev = NULL;
pmr->pr.pchText = NULL;
pmr->pr.cchText = 0;
pmr->pr.cchUnused = 0;
pmr->pr.psd = 0;
pmr->cchValid = 0L;
pmr->fLocked = FALSE;
pmr->cchLine = 0L;
pmr->pixLine = 0;
pmr->pixValid = 0;
pmr->cchBegin = 0;
pmr->cchEnd = 0;
}
return(pmr);
}
/* private function
*
* VOID InsertPrAfter(PPR ppr1, PPR ppr2)
*
* Inserts ppr2 after ppr1 in text order.
*/
private VOID InsertPrAfter(PPR ppr1, PPR ppr2)
{
DeleteTextOrder(ppr2);
// insert ppr2 after ppr1
if (ppr1->pprTextNext != NULL) {
ppr1->pprTextNext->pprTextPrev = ppr2;
}
ppr2->pprTextNext = ppr1->pprTextNext;
ppr2->pprTextPrev = ppr1;
ppr1->pprTextNext = ppr2;
}
/* private function
*
* VOID InsertPrBefore(PPR ppr1, PPR ppr2)
*
* Inserts ppr1 before ppr2 in text order.
*/
private VOID InsertPrBefore(PPR ppr1, PPR ppr2)
{
DeleteTextOrder(ppr1);
// insert ppr1 before ppr2
if (ppr2->pprTextPrev != NULL) {
ppr2->pprTextPrev->pprTextNext = ppr1;
}
ppr1->pprTextPrev = ppr2->pprTextPrev;
ppr2->pprTextPrev = ppr1;
ppr1->pprTextNext = ppr2;
}
/* private function
*
* VOID InsertPrAfterPhys(PPR ppr1, PPR ppr2)
*
* Inserts ppr2 after ppr1 in physical order.
*/
private VOID InsertPrAfterPhys(PPR ppr1, PPR ppr2)
{
// insert ppr2 after ppr1
if (ppr1->pprPhysNext != NULL) {
ppr1->pprPhysNext->pprPhysPrev = ppr2;
} else {
ppr1->psd->pprPhysLast = ppr2;
}
ppr2->pprPhysNext = ppr1->pprPhysNext;
ppr2->pprPhysPrev = ppr1;
ppr1->pprPhysNext = ppr2;
}
/* private function
*
* VOID DeleteTextOrder(PPR ppr)
*
* Deletes a piece record from the text order list. Does not otherwise
* affect the piece record.
*/
private VOID DeleteTextOrder(PPR ppr)
{
if (ppr->pprTextPrev != NULL) {
ppr->pprTextPrev->pprTextNext = ppr->pprTextNext;
}
if (ppr->pprTextNext != NULL) {
ppr->pprTextNext->pprTextPrev = ppr->pprTextPrev;
}
ppr->pprTextNext = NULL;
ppr->pprTextPrev = NULL;
}
/***********************************************************************
* Segment descriptor manipulation
***********************************************************************/
/* private function
*
* PSEGDESC CreateSegment(PED ped, OFFSET cchSize)
*
* Creates a segment of the requested size, and returns the descriptor
* for that segment. Initialises the segment to contain one piece,
* completely empty.
*/
private PSEGDESC CreateSegment(PED ped, OFFSET cchSize)
{
PPR ppr;
PSEGDESC psd;
SEL sel;
// get descriptor space
psd = AllocStore(ped, sizeof(SEGDESC));
if (psd == NULL)
return psd;
// initialise descriptor
psd->cchFree = cchSize;
psd->cchLen = cchSize;
psd->psdNext = psd->psdPrev = NULL;
// create the segment & piece for it
if (cchSize == 0) {
ppr = CreatePiecerec(ped, 0, NULL, psd);
sel = 0;
} else {
if (DosAllocSeg(cchSize, (PSEL)&sel, 0)) {
// notify of memory error
ppr = NULL;
} else {
ppr = CreatePiecerec(ped, cchSize, MAKEP(sel,0), psd);
}
}
psd->pprPhysLast = ppr;
psd->sel = sel;
// insert into segment list
BubbleUp(ped,psd);
return(psd);
}
/* private function
*
* VOID InsertSegAtEnd(PED ped, PSEGDESC psd)
*
* Moves the given segment descriptor to the end of the segment
* descriptor list. Used both to insert new descriptors into the list
* and to move descriptors which no free space in them.
*/
private VOID InsertSegAtEnd(PED ped, PSEGDESC psd)
{
// if it's already last on the list, get out quick.
if (psd == ped->psdLast)
return;
// delete from the segment list (if needed)
if (ped->psdCurrent == psd) {
ped->psdCurrent = psd->psdNext;
}
if (psd->psdPrev != NULL) {
psd->psdPrev->psdNext = psd->psdNext;
}
if (psd->psdNext != NULL) {
psd->psdNext->psdPrev = psd->psdPrev;
}
// insert at tail of list
psd->psdNext = NULL;
psd->psdPrev = ped->psdLast;
if (ped->psdLast != NULL)
ped->psdLast->psdNext = psd;
ped->psdLast = psd;
if (ped->psdCurrent == NULL)
ped->psdCurrent = psd;
}
/* private function
*
* VOID BubbleUp(PED ped, PSEGDESC psd)
*
* Moves the given segment descriptor to the beginning of the segment
* descriptor list. Used both to insert new descriptors into the list
* and to move descriptors which have significant new free space in
* them.
*/
private VOID BubbleUp(PED ped, PSEGDESC psd)
{
// if it's already first on the list, get out quick.
if (psd == ped->psdCurrent)
return;
// delete from the segment list (if needed)
if (ped->psdLast == psd) {
ped->psdLast = psd->psdPrev;
}
if (psd->psdPrev != NULL) {
psd->psdPrev->psdNext = psd->psdNext;
}
if (psd->psdNext != NULL) {
psd->psdNext->psdPrev = psd->psdPrev;
}
// insert at head of list
psd->psdPrev = NULL;
psd->psdNext = ped->psdCurrent;
if (ped->psdCurrent != NULL)
ped->psdCurrent->psdPrev = psd;
ped->psdCurrent = psd;
if (ped->psdLast == NULL)
ped->psdLast = psd;
}
/* private function
*
* VOID BubbleUpCheck(PED ped, PPR ppr, OFFSET cch)
*
* Causes a segment to be bubbled up to the front of the segment list
* if creating a free space greater than PIECE_MINDELETE.
*/
private VOID BubbleUpCheck(PED ped, PPR ppr, OFFSET cch)
{
if ((ppr->cchUnused < PIECE_MINDELETE) &&
((ppr->cchUnused + cch) >= PIECE_MINDELETE))
BubbleUp(ped, ppr->psd);
}
/***********************************************************************
* Space allocation / freeing functions
***********************************************************************/
/* private function
*
* BOOL ExtendSpace(PED ped, OFFSET cch)
*
* Attempt to extend the space available for storing pieces by cch characters.
* Searches down the segment list, trying to find a segment to reallocate
* by the given number of chars; if no such is found, then a new segment
* (of cch characters) is created. The extended or created segment is
* bubbled to the beginning of the segment list.
*
* This function should be called with a minimum cch of PIECE_GROWSIZE,
* though any value 1<=val<64K will work.
*/
private BOOL ExtendSpace(PED ped, OFFSET cch)
{
PSEGDESC psd;
for (psd = ped->psdCurrent; psd != NULL; psd = psd->psdNext) {
if ((((ULONG)psd->cchLen + (ULONG) cch) <= PIECE_MAXSEGSIZE)
&& (psd->cchLen != 0)) {
if (!(DosReallocSeg(psd->cchLen + cch, psd->sel))) {
psd->pprPhysLast->cchUnused += cch;
psd->cchFree += cch;
psd->cchLen += cch;
BubbleUp(ped, psd);
return(TRUE);
}
}
}
return(CreateSegment(ped,cch) != NULL);
}
/* private function
*
* PPR ScanFree(PED ped, OFFSET cchMin, OFFSET cchReq)
*
* Scans the list of segments containing piece information, attempting
* to find a piece with freespace >= cchReq; if no such can be found,
* then the piece with largest freespace >= cchMin will be returned;
* if no such piece exists then NULL will be returned.
* The search through the segment list ends when a segment with
* no free space is encountered.
*/
private PPR ScanFree(PED ped, OFFSET cchMin, OFFSET cchReq)
{
PSEGDESC psd;
PPR ppr;
OFFSET cchMax;
PPR pprMax;
OFFSET cch;
pprMax = NULL;
cchMax = 0;
for (psd = ped->psdCurrent; psd != NULL; psd = psd->psdNext) {
// get out when hit full segment
if (psd->cchFree == 0)
break;
// skip search of nearly-full seg
if ((psd->cchFree<cchMin) && (psd->cchFree<cchReq))
continue;
for (ppr = psd->pprPhysLast; ppr != NULL; ppr=ppr->pprPhysPrev) {
cch = ppr->cchUnused;
if (cch >= cchReq) {
return(ppr);
} else if (cch >= cchMax) {
cchMax = cch;
pprMax = ppr;
}
}
}
if (cchMax >= cchMin)
return pprMax;
else
return NULL;
}
/* private function
*
* PPR FindFreeSpace(PED ped, OFFSET cchMin, OFFSET cchReq)
*
* Searches segments containing piece information to find a piece
* with free space >= cchMin; >= cchReq if possible. The ancillary
* function ScanFree is used to find such space; if it reports failure,
* then more space is obtained from the operating system and the
* Scan is tried again.
*/
private PPR FindFreeSpace(PED ped, OFFSET cchMin, OFFSET cchReq)
{
PPR ppr;
if ((ppr = ScanFree(ped, cchMin, cchReq)) != NULL)
return(ppr);
if (!(ExtendSpace(ped, (OFFSET)max((ULONG)cchReq,PIECE_GROWSIZE))))
return(NULL);
return(ScanFree(ped, cchMin, cchReq));
}
/* private function
*
* VOID Reserve(PPR ppr, OFFSET cch)
*
* Extends the text allocation of a given piece by cch characters.
* Adjusts the text and unused counts in the piece, and the free space
* count in the segment. Doesn't allow negative reserve (use Unreserve)
*/
private VOID Reserve(PPR ppr, OFFSET cch)
{
ppr->cchUnused -= cch;
ppr->cchText += cch;
ppr->psd->cchFree -=cch;
}
/* private function
*
* VOID UnReserve(PPR ppr, OFFSET cch)
*
* Retracts the text allocation of a given piece by cch characters.
* Adjusts the text and unused counts in the piece, and the free space
* count in the segment. Complement of Reserve.
*/
private VOID UnReserve(PPR ppr, OFFSET cch)
{
ppr->cchUnused += cch;
ppr->cchText -= cch;
ppr->psd->cchFree += cch;
}
/* private function
*
* VOID DestroyPiece(PED ped, PPR ppr)
*
* Given a piece, potentially containing text, deletes the entire piece.
* Deletes the piece from the text order list. Merges the piece with
* the physically previous piece, if there is such.
*/
private VOID DestroyPiece(PED ped, PPR ppr)
{
if (fIsMarker(ppr)) {
if (!((PMR)ppr)->fLocked) {
DeleteTextOrder(ppr);
FreeStore(ped,ppr);
}
} else {
BubbleUpCheck(ped,ppr,ppr->cchText);
UnReserve(ppr,ppr->cchText);
DeleteTextOrder(ppr);
if (ppr->pprPhysPrev != NULL) {
ppr->pprPhysPrev->cchUnused += ppr->cchUnused;
ppr->pprPhysPrev->pprPhysNext = ppr->pprPhysNext;
if (ppr->pprPhysNext != NULL) {
ppr->pprPhysNext->pprPhysPrev = ppr->pprPhysPrev;
} else {
ppr->psd->pprPhysLast = ppr->pprPhysPrev;
}
FreeStore(ped,ppr);
}
}
}
/***********************************************************************
* External interface functions
***********************************************************************/
/* public function
*
* IPT PieceLength(PPR ppr)
*
* returns the size of a piece.
*/
public IPT PieceLength(PPR ppr)
{
return((IPT)ppr->cchText);
}
/* public function
*
* PPR CreateSizedPiece(PED ped, OFFSET cchMin, OFFSET cchReq)
*
* Allocates a piece of size >= cchMin; efforts will be made to ensure
* that the piece is of size >= cchReq but it isn't guaranteed.
* It is the caller's responsibility to insert this piece into text
* order lists; it will be on the correct physical order lists.
* Note that it is perfectly valid to specify cchMin > cchReq; this
* will attempt to create a piece of size >= cchReq; if this fails,
* a "big" piece will be created.
*/
public PPR CreateSizedPiece(PED ped, OFFSET cchMin, OFFSET cchReq)
{
PPR pprExisting, ppr;
pprExisting = FindFreeSpace(ped, cchMin, cchReq);
if (pprExisting == NULL)
return NULL;
ppr = SplitPiece(ped, pprExisting, pprExisting->cchText);
DeleteTextOrder(ppr);
return(ppr);
}
/* public function
*
* PCHAR InsEndChars(PED ped, PPPR pppr, POFFSET pcch)
*
* reserves *pcch characters at the end of the piece described by
* *pppr. If there isn't enough space at the end of that segment,
* a new piece is created and linked as next in text order after
* the provided segment. The function doesn't guarantee that
* *pcch characters will be reserved; the amount actually reserved
* is returned in *pcch. It is guaranteed that at least one character
* will be reserved so progress will be made; anything else is
* considered a failure and the function will return NULL (as well
* as a zero *pcch). Since the piece record may change, the
* function also modifies the pppr to guarantee that the piece
* out of which text space was allocated is available to the caller.
*/
public PCHAR InsEndChars(PED ped, PPPR pppr, POFFSET pcch)
{
PCHAR pch;
PPR ppr2;
OFFSET cchRes;
cchRes = min((*pppr)->cchUnused, *pcch);
if (cchRes > 0) {
pch = (*pppr)->pchText + (*pppr)->cchText;
*pcch = cchRes;
Reserve((*pppr),cchRes);
} else {
*pcch = (OFFSET)min((ULONG)*pcch, PIECE_MAXCREATE);
ppr2 = CreateSizedPiece(ped, PIECE_MINCREATE, *pcch);
if (ppr2 == NULL) {
*pcch = 0;
pch = NULL;
} else {
InsertPrAfter((*pppr),ppr2);
*pppr = ppr2;
pch = ppr2->pchText;
*pcch = min(*pcch, ppr2->cchUnused);
Reserve(ppr2,*pcch);
}
}
return(pch);
}
/* public function
*
* PMR InsEndMarker(PED ped, PPR ppr, USHORT fFlags)
*
* Inserts a marker piece after the piece ppr, with flags fFlags.
* Returns a pointer to the marker piece inserted.
*/
public PMR InsEndMarker(PED ped, PPR ppr, USHORT fFlags)
{
PMR pmr;
pmr = CreateMarkerPiece(ped);
if (pmr != NULL) {
pmr->fFlags = fFlags;
InsertPrAfter(ppr,(PPR)pmr);
}
return(pmr);
}
/* public function
*
* PMR InsBeforeMarker(PED ped, PPR ppr, USHORT fFlags)
*
* Inserts a marker piece before the piece ppr, with flags fFlags.
* Returns a pointer to the marker piece inserted.
*/
public PMR InsBeforeMarker(PED ped, PPR ppr, USHORT fFlags)
{
PMR pmr;
pmr = CreateMarkerPiece(ped);
if (pmr != NULL) {
pmr->fFlags = fFlags;
InsertPrBefore((PPR)pmr,ppr);
}
return(pmr);
}
/* public function
*
* PPR DelStartChars(PED ped, PPR ppr, OFFSET cch)
*
* Deletes cch characters at the beginning of a piece.
* Returns pointer to piece record containing text following deletion, or
* to last piece record in list if deleting last piece in the list.
* PPR may be invalid after deletion.
* If cch == 0 on a piece containing text, does nothing.
*/
public PPR DelStartChars(PED ped, PPR ppr, OFFSET cch)
{
PPR ppr2;
if (cch == ppr->cchText) {
ppr2 = (ppr->pprTextNext != NULL)
? ppr->pprTextNext
: ppr->pprTextPrev;
} else {
if (cch == 0) {
return(ppr);
}
ppr2 = SplitPiece(ped, ppr, cch);
}
DestroyPiece(ped, ppr);
return(ppr2);
}
/* public function
*
* PPR DelEndChars(PED ped, PPR ppr, OFFSET cch)
*
* Deletes cch characters from the end of a piece. Returns piece containing
* text immediately following deletion, or NULL if no next pieces exist.
* If cch == 0, gets out as fast as possible.
* Note that the PPR passed in may be invalid after this function.
*/
public PPR DelEndChars(PED ped, PPR ppr, OFFSET cch)
{
PPR ppr2;
if (ppr->cchText == cch) {
ppr2 = ppr->pprTextNext;
DestroyPiece(ped,ppr);
return(ppr2);
} else {
BubbleUpCheck(ped, ppr, cch);
UnReserve(ppr, cch);
return(ppr->pprTextNext);
}
}
/* public function
*
* PPR DelMarker(PED ped, PMR pmr)
*
* Deletes a marker piece. Returns the next piece in text order if any,
* or previous piece if no next piece exists.
*/
public PPR DelMarker(PED ped, PMR pmr)
{
PPR ppr;
ppr = pmr->pr.pprTextNext;
if (ppr == NULL) {
ppr = pmr->pr.pprTextPrev;
}
DestroyPiece(ped,(PPR)pmr);
return(ppr);
}
/* public function
*
* PPR SplitPiece(PED ped, PPR ppr, OFFSET cch)
*
* splits a piece into two pieces by shortening the existing piece to
* cch characters; all remaining characters and any free space are
* given to a new piece. The new piece is returned from this function.
* Boundary conditions of cch==0 and cch==size are legal and split
* off empty pieces.
* Returns new, right-hand, piece.
*/
public PPR SplitPiece(PED ped, PPR ppr, OFFSET cch)
{
PPR ppr2;
OFFSET cchMoved;
ppr2 = CreatePiecerec(ped, ppr->cchUnused+ppr->cchText-cch,
ppr->pchText+cch, ppr->psd);
InsertPrAfterPhys(ppr,ppr2);
InsertPrAfter(ppr,ppr2);
cchMoved = ppr->cchText-cch;
UnReserve(ppr,cchMoved);
Reserve(ppr2,cchMoved);
ppr->cchUnused = 0;
return(ppr2);
}
/* public function
*
* PPR ConditionalSplitPiece(PED ped, PPR ppr, OFFSET cch)
*
* if ppr/cch doesn't already point to a split, splits the piece
* at the appropriate place. Either way, function returns the piece
* immediately following the split. Returns NULL if the ppr/cch point
* past the end of the text. If possible, works to minimise proliferation
* of unneeded pieces by shifting piece boundaries rather than creating a
* new piece.
*/
public PPR ConditionalSplitPiece(PED ped, PPR ppr, OFFSET cch)
{
PPR pprD;
if (cch == 0)
return(ppr);
if (cch >= ppr->cchText)
return(ppr->pprTextNext);
pprD = ppr->pprTextPrev;
if ((ppr->pprPhysPrev == pprD) && (pprD->cchUnused == 0)) {
ppr->pchText += cch;
ppr->cchText -= cch;
pprD->cchText += cch;
if (ppr->cchText == 0) {
DestroyPiece(ped, ppr);
return(pprD->pprTextNext);
} else {
return(ppr);
}
}
return(SplitPiece(ped,ppr,cch));
}
/* public function
*
* PPR AppendSegment(PED ped, PPR ppr, SEL sel, OFFSET cch)
*
* Given a selector for a segment, and the length of that segment,
* inserts the segment's text immediately after ppr. After the
* append, the segment is assumed to be under the piece manager's
* control and will be used to store piece text like any other segment.
*/
public PPR AppendSegment(PED ped, PPR ppr, SEL sel, OFFSET cch)
{
PSEGDESC psd;
PPR pprNew;
psd = AllocStore(ped, sizeof(SEGDESC));
if (psd == NULL) {
// out of memory error
return(NULL);
}
psd->cchFree = 0;
psd->cchLen = cch;
psd->psdNext = psd->psdPrev = NULL;
pprNew = CreatePiecerec(ped, cch, MAKEP(sel,0), psd);
InsertPrAfter(ppr, pprNew);
psd->pprPhysLast = ppr;
psd->sel = sel;
InsertSegAtEnd(ped, psd);
return(pprNew);
}
/* public function
*
* PPR PprOfOffset(PPR ppr, PIPT poffCh)
*
* Given a ppr, scans from the beginning of the pr until the pr
* *poffCh later has been found. Returns that ppr, and the offset
* into that piece. If an offset that is logically between
* pieces is specified, then to piece following the ipt is
* returned, except for the last piece in a list. If an ipt greater
* than the length of text is supplied, then the last piece in the
* list is returned and the offset is truncated to the length
* of that piece.
*/
public PPR PprOfOffset(PPR ppr, PIPT poffCh)
{
if (ppr == NULL) {
*poffCh = 0;
return(NULL);
}
while ((ppr->pprTextNext != NULL) && (*poffCh >= ppr->cchText)) {
*poffCh -= ppr->cchText;
ppr = ppr->pprTextNext;
}
*poffCh = max((IPT)0,min(*poffCh, (IPT)(ppr->cchText)));
return(ppr);
}
/* public function
*
* BOOL fIsMarker(PPR ppr)
*
* Given a ppr, returns whether it points to a marker or ordinary piece record
*/
public BOOL fIsMarker(PPR ppr)
{
if (ppr == NULL) {
return(FALSE);
}
return(ppr->pchText == NULL);
}
/* public function
*
* PPR SkipMarkers(PPR ppr, USHORT fMask)
*
* Given a ppr, skips zero or more marker pieces which match the mask
* to get to the first non-matching or non-marker piece (which may be the
* value passed in.) Returns that
* piece. 'Match' is defined as 'flag bits, when anded with fMask, yield
* a nonzero value.'
*/
public PPR SkipMarkers(PPR ppr, USHORT fMask)
{
while (fIsMarker(ppr) &&
(ppr->pprTextNext != NULL) &&
(((PMR)ppr)->fFlags & fMask)) {
ppr = ppr->pprTextNext;
}
return(ppr);
}
/* public function
*
* PPR SkipBackText(PPR ppr, USHORT fMask)
*
* Given a ppr, scans backwards across zero or more pieces to get to the first
* marker piece (which may be the value passed in) which matches the
* mask (as in SkipMarkers.) Returns that piece.
*/
public PPR SkipBackText(PPR ppr, USHORT fMask)
{
while ((!fIsMarker(ppr) || !(((PMR)ppr)->fFlags & fMask)) &&
(ppr->pprTextPrev != NULL)) {
ppr = ppr->pprTextPrev;
}
return(ppr);
}
/* public function
*
* PPR SkipText(PPR ppr, USHORT fMask)
*
* Given a ppr, scans forwards across zero or more pieces to get to the first
* marker piece (which may be the value passed in) which matches the
* mask (as in SkipMarkers.) Returns that piece.
*/
public PPR SkipText(PPR ppr, USHORT fMask)
{
while ((!fIsMarker(ppr) || !(((PMR)ppr)->fFlags & fMask)) &&
(ppr->pprTextNext != NULL)) {
ppr = ppr->pprTextNext;
}
return(ppr);
}
/* public function
*
* BOOL LockMarker(PMR pmr, BOOL fLocked)
*
* Given a marker piece, locks or unlocks that piece. Returns the old lock
* status of the piece.
*/
public BOOL LockMarker(PMR pmr, BOOL fLocked)
{
BOOL fOld;
fOld = pmr->fLocked;
pmr->fLocked = fLocked;
return(fOld);
}
/* public function
*
* CHAR CharOfPointer(PPR ppr, OFFSET off)
*
* Gives character at a given pointer. Returns \0 if the pointer is invalid.
*/
public CHAR CharOfPointer(PPR ppr, OFFSET off)
{
return(((off > ppr->cchText) || (off < 0))
? (CHAR)'\0'
: ((fIsMarker(ppr))
? (CHAR)'\0'
: (*(ppr->pchText+off))));
}
/* public function
*
* CHAR AdvancePointer(PPPR pppr, POFFSET poff)
*
* Advances a ppr/offset pair by one.
*/
public CHAR AdvancePointer(PPPR pppr, POFFSET poff)
{
(*poff) += 1;
if (*poff >= (*pppr)->cchText) {
if ((*pppr)->pprTextNext == NULL) {
(*poff) -= 1;
return((CHAR)'\0');
} else {
*pppr = (*pppr)->pprTextNext;
*poff = 0;
return((fIsMarker(*pppr))?(CHAR)'\0':*((*pppr)->pchText));
}
} else
return(*((*pppr)->pchText + (*poff)));
}
/* public function
*
* CHAR RetreatPointer(PPPR pppr, POFFSET poff)
*
* Retracts a ppr/offset pair by one.
*/
public CHAR RetreatPointer(PPPR pppr, POFFSET poff)
{
if (*poff == 0) {
*pppr = (*pppr)->pprTextPrev;
*poff = ((*pppr)->cchText==0) ? 0 : (*pppr)->cchText-1;
} else {
(*poff) -= 1;
}
return((fIsMarker(*pppr))
? (CHAR)'\0'
: (*((*pppr)->pchText + (*poff))));
}
/***********************************************************************
* Initialisation
*
* These functions should only be called at startup. They initialise
* the piece manager.
***********************************************************************/
/* public function
*
* PPR PieceInit(PED ped, OFFSET offSize)
*
* Initialise piece management
* Primitive memory manager should be initialised first.
*
* Returns the empty piece record created for the empty segment.
*/
public PPR PieceInit(PED ped, OFFSET offSize)
{
ped->psdCurrent = NULL;
ped->psdLast = NULL;
CreateSegment(ped,offSize);
if (ped->psdCurrent == NULL)
return NULL;
else
return ped->psdCurrent->pprPhysLast;
}