home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / NOTEPAD2.ZIP / MPIECE.C < prev    next >
C/C++ Source or Header  |  1989-02-08  |  29KB  |  980 lines

  1. /*
  2.  * mpiece.c -- Piece management
  3.  *
  4.  * Created by Microsoft Corporation, 1989
  5.  */
  6.  
  7. #include <os2.h>
  8. #include "mtypes.h"
  9. #include "mfuncs.h"
  10.  
  11. /***********************************************************************
  12. * Internal forward declarations
  13. ***********************************************************************/
  14.  
  15. private PPR CreatePiecerec(PED ped, OFFSET cch, PCHAR pch, PSEGDESC psd);
  16. private PSEGDESC CreateSegment(PED ped, OFFSET cchSize);
  17. private VOID BubbleUp(PED ped, PSEGDESC psd);
  18. private VOID DeleteTextOrder(PPR ppr);
  19.  
  20. /***********************************************************************
  21. * Piece record manipulation
  22. ***********************************************************************/
  23.  
  24. /* private function
  25.  *
  26.  * PPR CreatePiecerec(PED ped, OFFSET cch, PCHAR pch, PSEGDESC psd)
  27.  *
  28.  * Creates a new empty piece record for cch characters at address
  29.  * pch.  Freespace count in segment must be maintained elsewhere.
  30.  */
  31.  
  32. private PPR CreatePiecerec(PED ped, OFFSET cch, PCHAR pch, PSEGDESC psd)
  33. {
  34.         PPR ppr;
  35.  
  36.         ppr = AllocStore(ped, sizeof(PIECE));
  37.         if (ppr == NULL) {
  38.                 return(ppr);
  39.         }
  40.  
  41.         ppr->pprTextNext = NULL;
  42.         ppr->pprTextPrev = NULL;
  43.         ppr->pprPhysNext = NULL;
  44.         ppr->pprPhysPrev = NULL;
  45.         ppr->pchText = pch;
  46.         ppr->cchText = 0;
  47.         ppr->cchUnused = cch;
  48.         ppr->psd = psd;
  49.  
  50.         return(ppr);
  51. }
  52.  
  53. /* private function
  54.  *
  55.  * PMR CreateMarkerPiece(PED ped)
  56.  *
  57.  * Creates a new marker piece record.  Returns NULL on failure.
  58.  */
  59.  
  60. private PMR CreateMarkerPiece(PED ped)
  61. {
  62.         PMR pmr;
  63.  
  64.         pmr = AllocStore(ped, sizeof(MARKERPIECE));
  65.         if (pmr != NULL) {
  66.                 pmr->pr.pprTextNext = NULL;
  67.                 pmr->pr.pprTextPrev = NULL;
  68.                 pmr->pr.pprPhysNext = NULL;
  69.                 pmr->pr.pprPhysPrev = NULL;
  70.                 pmr->pr.pchText = NULL;
  71.                 pmr->pr.cchText = 0;
  72.                 pmr->pr.cchUnused = 0;
  73.                 pmr->pr.psd = 0;
  74.                 pmr->cchValid = 0L;
  75.                 pmr->fLocked = FALSE;
  76.                 pmr->cchLine = 0L;
  77.                 pmr->pixLine = 0;
  78.                 pmr->pixValid = 0;
  79.                 pmr->cchBegin = 0;
  80.                 pmr->cchEnd = 0;
  81.         }
  82.         return(pmr);
  83. }
  84.  
  85. /* private function
  86.  *
  87.  * VOID InsertPrAfter(PPR ppr1, PPR ppr2)
  88.  *
  89.  * Inserts ppr2 after ppr1 in text order.
  90.  */
  91. private VOID InsertPrAfter(PPR ppr1, PPR ppr2)
  92. {
  93.         DeleteTextOrder(ppr2);
  94.  
  95.         // insert ppr2 after ppr1
  96.         if (ppr1->pprTextNext != NULL) {
  97.                 ppr1->pprTextNext->pprTextPrev = ppr2;
  98.         }
  99.         ppr2->pprTextNext = ppr1->pprTextNext;
  100.         ppr2->pprTextPrev = ppr1;
  101.         ppr1->pprTextNext = ppr2;
  102. }
  103.  
  104.  
  105.  
  106. /* private function
  107.  *
  108.  * VOID InsertPrBefore(PPR ppr1, PPR ppr2)
  109.  *
  110.  * Inserts ppr1 before ppr2 in text order.
  111.  */
  112. private VOID InsertPrBefore(PPR ppr1, PPR ppr2)
  113. {
  114.         DeleteTextOrder(ppr1);
  115.  
  116.         // insert ppr1 before ppr2
  117.         if (ppr2->pprTextPrev != NULL) {
  118.                 ppr2->pprTextPrev->pprTextNext = ppr1;
  119.         }
  120.         ppr1->pprTextPrev = ppr2->pprTextPrev;
  121.         ppr2->pprTextPrev = ppr1;
  122.         ppr1->pprTextNext = ppr2;
  123. }
  124.  
  125. /* private function
  126.  *
  127.  * VOID InsertPrAfterPhys(PPR ppr1, PPR ppr2)
  128.  *
  129.  * Inserts ppr2 after ppr1 in physical order.
  130.  */
  131. private VOID InsertPrAfterPhys(PPR ppr1, PPR ppr2)
  132. {
  133.  
  134.         // insert ppr2 after ppr1
  135.         if (ppr1->pprPhysNext != NULL) {
  136.                 ppr1->pprPhysNext->pprPhysPrev = ppr2;
  137.         } else {
  138.                 ppr1->psd->pprPhysLast = ppr2;
  139.         }
  140.         ppr2->pprPhysNext = ppr1->pprPhysNext;
  141.         ppr2->pprPhysPrev = ppr1;
  142.         ppr1->pprPhysNext = ppr2;
  143. }
  144.  
  145.  
  146. /* private function
  147.  *
  148.  * VOID DeleteTextOrder(PPR ppr)
  149.  *
  150.  * Deletes a piece record from the text order list.  Does not otherwise
  151.  * affect the piece record.
  152.  */
  153. private VOID DeleteTextOrder(PPR ppr)
  154. {
  155.         if (ppr->pprTextPrev != NULL) {
  156.                 ppr->pprTextPrev->pprTextNext = ppr->pprTextNext;
  157.         }
  158.         if (ppr->pprTextNext != NULL) {
  159.                 ppr->pprTextNext->pprTextPrev = ppr->pprTextPrev;
  160.         }
  161.         ppr->pprTextNext = NULL;
  162.         ppr->pprTextPrev = NULL;
  163. }
  164.  
  165. /***********************************************************************
  166. * Segment descriptor manipulation
  167. ***********************************************************************/
  168.  
  169. /* private function
  170.  *
  171.  * PSEGDESC CreateSegment(PED ped, OFFSET cchSize)
  172.  *
  173.  * Creates a segment of the requested size, and returns the descriptor
  174.  * for that segment.  Initialises the segment to contain one piece,
  175.  * completely empty.
  176.  */
  177.  
  178. private PSEGDESC CreateSegment(PED ped, OFFSET cchSize)
  179. {
  180.         PPR ppr;
  181.         PSEGDESC psd;
  182.         SEL sel;
  183.  
  184.         // get descriptor space
  185.         psd = AllocStore(ped, sizeof(SEGDESC));
  186.         if (psd == NULL)
  187.                 return psd;
  188.  
  189.         // initialise descriptor
  190.         psd->cchFree = cchSize;
  191.         psd->cchLen = cchSize;
  192.         psd->psdNext = psd->psdPrev = NULL;
  193.  
  194.         // create the segment & piece for it
  195.         if (cchSize == 0) {
  196.                 ppr = CreatePiecerec(ped, 0, NULL, psd);
  197.                 sel = 0;
  198.         } else {
  199.                 if (DosAllocSeg(cchSize, (PSEL)&sel, 0)) {
  200.                         // notify of memory error
  201.                         ppr = NULL;
  202.                 } else {
  203.                         ppr = CreatePiecerec(ped, cchSize, MAKEP(sel,0), psd);
  204.                 }
  205.         }
  206.         psd->pprPhysLast = ppr;
  207.         psd->sel = sel;
  208.  
  209.         // insert into segment list
  210.         BubbleUp(ped,psd);
  211.         return(psd);
  212. }
  213.  
  214.  
  215. /* private function
  216.  *
  217.  * VOID InsertSegAtEnd(PED ped, PSEGDESC psd)
  218.  *
  219.  * Moves the given segment descriptor to the end of the segment
  220.  * descriptor list.  Used both to insert new descriptors into the list
  221.  * and to move descriptors which no free space in them.
  222.  */
  223. private VOID InsertSegAtEnd(PED ped, PSEGDESC psd)
  224. {
  225.         // if it's already last on the list, get out quick.
  226.  
  227.         if (psd == ped->psdLast)
  228.                 return;
  229.  
  230.         // delete from the segment list (if needed)
  231.  
  232.         if (ped->psdCurrent == psd) {
  233.                 ped->psdCurrent = psd->psdNext;
  234.         }
  235.         if (psd->psdPrev != NULL) {
  236.                 psd->psdPrev->psdNext = psd->psdNext;
  237.         }
  238.         if (psd->psdNext != NULL) {
  239.                 psd->psdNext->psdPrev = psd->psdPrev;
  240.         }
  241.  
  242.         // insert at tail of list
  243.         psd->psdNext = NULL;
  244.         psd->psdPrev = ped->psdLast;
  245.         if (ped->psdLast != NULL)
  246.                 ped->psdLast->psdNext = psd;
  247.         ped->psdLast = psd;
  248.         if (ped->psdCurrent == NULL)
  249.                 ped->psdCurrent = psd;
  250. }
  251.  
  252. /* private function
  253.  *
  254.  * VOID BubbleUp(PED ped, PSEGDESC psd)
  255.  *
  256.  * Moves the given segment descriptor to the beginning of the segment
  257.  * descriptor list.  Used both to insert new descriptors into the list
  258.  * and to move descriptors which have significant new free space in
  259.  * them.
  260.  */
  261. private VOID BubbleUp(PED ped, PSEGDESC psd)
  262. {
  263.         // if it's already first on the list, get out quick.
  264.  
  265.         if (psd == ped->psdCurrent)
  266.                 return;
  267.  
  268.         // delete from the segment list (if needed)
  269.  
  270.         if (ped->psdLast == psd) {
  271.                 ped->psdLast = psd->psdPrev;
  272.         }
  273.         if (psd->psdPrev != NULL) {
  274.                 psd->psdPrev->psdNext = psd->psdNext;
  275.         }
  276.         if (psd->psdNext != NULL) {
  277.                 psd->psdNext->psdPrev = psd->psdPrev;
  278.         }
  279.  
  280.         // insert at head of list
  281.         psd->psdPrev = NULL;
  282.         psd->psdNext = ped->psdCurrent;
  283.         if (ped->psdCurrent != NULL)
  284.                 ped->psdCurrent->psdPrev = psd;
  285.         ped->psdCurrent = psd;
  286.         if (ped->psdLast == NULL)
  287.                 ped->psdLast = psd;
  288. }
  289.  
  290. /* private function
  291.  *
  292.  * VOID BubbleUpCheck(PED ped, PPR ppr, OFFSET cch)
  293.  *
  294.  * Causes a segment to be bubbled up to the front of the segment list
  295.  * if creating a free space greater than PIECE_MINDELETE.
  296.  */
  297. private VOID BubbleUpCheck(PED ped, PPR ppr, OFFSET cch)
  298. {
  299.         if ((ppr->cchUnused < PIECE_MINDELETE) &&
  300.                 ((ppr->cchUnused + cch) >= PIECE_MINDELETE))
  301.                         BubbleUp(ped, ppr->psd);
  302. }
  303.  
  304.  
  305. /***********************************************************************
  306. * Space allocation / freeing functions
  307. ***********************************************************************/
  308.  
  309. /* private function
  310.  *
  311.  * BOOL ExtendSpace(PED ped, OFFSET cch)
  312.  *
  313.  * Attempt to extend the space available for storing pieces by cch characters.
  314.  * Searches down the segment list, trying to find a segment to reallocate
  315.  * by the given number of chars; if no such is found, then a new segment
  316.  * (of cch characters) is created.  The extended or created segment is
  317.  * bubbled to the beginning of the segment list.
  318.  *
  319.  * This function should be called with a minimum cch of PIECE_GROWSIZE,
  320.  * though any value 1<=val<64K will work.
  321.  */
  322. private BOOL ExtendSpace(PED ped, OFFSET cch)
  323. {
  324.         PSEGDESC psd;
  325.  
  326.         for (psd = ped->psdCurrent; psd != NULL; psd = psd->psdNext) {
  327.                 if ((((ULONG)psd->cchLen + (ULONG) cch) <= PIECE_MAXSEGSIZE)
  328.                     && (psd->cchLen != 0)) {
  329.                         if (!(DosReallocSeg(psd->cchLen + cch, psd->sel))) {
  330.                                 psd->pprPhysLast->cchUnused += cch;
  331.                                 psd->cchFree += cch;
  332.                                 psd->cchLen += cch;
  333.                                 BubbleUp(ped, psd);
  334.                                 return(TRUE);
  335.                         }
  336.                 }
  337.         }
  338.         return(CreateSegment(ped,cch) != NULL);
  339. }
  340.  
  341.  
  342. /* private function
  343.  *
  344.  * PPR ScanFree(PED ped, OFFSET cchMin, OFFSET cchReq)
  345.  *
  346.  * Scans the list of segments containing piece information, attempting
  347.  * to find a piece with freespace >= cchReq; if no such can be found,
  348.  * then the piece with largest freespace >= cchMin will be returned;
  349.  * if no such piece exists then NULL will be returned.
  350.  * The search through the segment list ends when a segment with
  351.  * no free space is encountered.
  352.  */
  353.  
  354. private PPR ScanFree(PED ped, OFFSET cchMin, OFFSET cchReq)
  355. {
  356.         PSEGDESC psd;
  357.         PPR ppr;
  358.         OFFSET cchMax;
  359.         PPR pprMax;
  360.         OFFSET cch;
  361.  
  362.         pprMax = NULL;
  363.         cchMax = 0;
  364.  
  365.         for (psd = ped->psdCurrent; psd != NULL; psd = psd->psdNext) {
  366.                 // get out when hit full segment
  367.                 if (psd->cchFree == 0)
  368.                         break;
  369.                 // skip search of nearly-full seg
  370.                 if ((psd->cchFree<cchMin) && (psd->cchFree<cchReq))
  371.                         continue;
  372.                 for (ppr = psd->pprPhysLast; ppr != NULL; ppr=ppr->pprPhysPrev) {
  373.                         cch = ppr->cchUnused;
  374.                         if (cch >= cchReq) {
  375.                                 return(ppr);
  376.                         } else if (cch >= cchMax) {
  377.                                 cchMax = cch;
  378.                                 pprMax = ppr;
  379.                         }
  380.                 }
  381.         }
  382.         if (cchMax >= cchMin)
  383.                 return pprMax;
  384.         else
  385.                 return NULL;
  386. }
  387.  
  388. /* private function
  389.  *
  390.  * PPR FindFreeSpace(PED ped, OFFSET cchMin, OFFSET cchReq)
  391.  *
  392.  * Searches segments containing piece information to find a piece
  393.  * with free space >= cchMin; >= cchReq if possible.  The ancillary
  394.  * function ScanFree is used to find such space; if it reports failure,
  395.  * then more space is obtained from the operating system and the
  396.  * Scan is tried again.
  397.  */
  398.  
  399. private PPR FindFreeSpace(PED ped, OFFSET cchMin, OFFSET cchReq)
  400. {
  401.         PPR ppr;
  402.  
  403.         if ((ppr = ScanFree(ped, cchMin, cchReq)) != NULL)
  404.                 return(ppr);
  405.  
  406.         if (!(ExtendSpace(ped, (OFFSET)max((ULONG)cchReq,PIECE_GROWSIZE))))
  407.                 return(NULL);
  408.  
  409.         return(ScanFree(ped, cchMin, cchReq));
  410. }
  411.  
  412. /* private function
  413.  *
  414.  * VOID Reserve(PPR ppr, OFFSET cch)
  415.  *
  416.  * Extends the text allocation of a given piece by cch characters.
  417.  * Adjusts the text and unused counts in the piece, and the free space
  418.  * count in the segment.  Doesn't allow negative reserve (use Unreserve)
  419.  */
  420. private VOID Reserve(PPR ppr, OFFSET cch)
  421. {
  422.         ppr->cchUnused -= cch;
  423.         ppr->cchText += cch;
  424.         ppr->psd->cchFree -=cch;
  425. }
  426.  
  427. /* private function
  428.  *
  429.  * VOID UnReserve(PPR ppr, OFFSET cch)
  430.  *
  431.  * Retracts the text allocation of a given piece by cch characters.
  432.  * Adjusts the text and unused counts in the piece, and the free space
  433.  * count in the segment.  Complement of Reserve.
  434.  */
  435. private VOID UnReserve(PPR ppr, OFFSET cch)
  436. {
  437.         ppr->cchUnused += cch;
  438.         ppr->cchText -= cch;
  439.         ppr->psd->cchFree += cch;
  440. }
  441.  
  442.  
  443. /* private function
  444.  *
  445.  * VOID DestroyPiece(PED ped, PPR ppr)
  446.  *
  447.  * Given a piece, potentially containing text, deletes the entire piece.
  448.  * Deletes the piece from the text order list.  Merges the piece with
  449.  * the physically previous piece, if there is such.
  450.  */
  451. private VOID DestroyPiece(PED ped, PPR ppr)
  452. {
  453.     if (fIsMarker(ppr)) {
  454.         if (!((PMR)ppr)->fLocked) {
  455.             DeleteTextOrder(ppr);
  456.             FreeStore(ped,ppr);
  457.         }
  458.     } else {
  459.         BubbleUpCheck(ped,ppr,ppr->cchText);
  460.         UnReserve(ppr,ppr->cchText);
  461.         DeleteTextOrder(ppr);
  462.         if (ppr->pprPhysPrev != NULL) {
  463.             ppr->pprPhysPrev->cchUnused += ppr->cchUnused;
  464.             ppr->pprPhysPrev->pprPhysNext = ppr->pprPhysNext;
  465.             if (ppr->pprPhysNext != NULL) {
  466.                 ppr->pprPhysNext->pprPhysPrev = ppr->pprPhysPrev;
  467.             } else {
  468.                 ppr->psd->pprPhysLast = ppr->pprPhysPrev;
  469.             }
  470.             FreeStore(ped,ppr);
  471.         }
  472.     }
  473. }
  474.  
  475. /***********************************************************************
  476. * External interface functions
  477. ***********************************************************************/
  478.  
  479. /* public function
  480.  *
  481.  * IPT PieceLength(PPR ppr)
  482.  *
  483.  * returns the size of a piece.
  484.  */
  485. public IPT PieceLength(PPR ppr)
  486. {
  487.         return((IPT)ppr->cchText);
  488. }
  489.  
  490.  
  491. /* public function
  492.  *
  493.  * PPR CreateSizedPiece(PED ped, OFFSET cchMin, OFFSET cchReq)
  494.  *
  495.  * Allocates a piece of size >= cchMin; efforts will be made to ensure
  496.  * that the piece is of size >= cchReq but it isn't guaranteed.
  497.  * It is the caller's responsibility to insert this piece into text
  498.  * order lists; it will be on the correct physical order lists.
  499.  * Note that it is perfectly valid to specify cchMin > cchReq; this
  500.  * will attempt to create a piece of size >= cchReq; if this fails,
  501.  * a "big" piece will be created.
  502.  */
  503.  
  504. public PPR CreateSizedPiece(PED ped, OFFSET cchMin, OFFSET cchReq)
  505. {
  506.         PPR pprExisting, ppr;
  507.  
  508.         pprExisting = FindFreeSpace(ped, cchMin, cchReq);
  509.  
  510.         if (pprExisting == NULL)
  511.                 return NULL;
  512.  
  513.         ppr = SplitPiece(ped, pprExisting, pprExisting->cchText);
  514.         DeleteTextOrder(ppr);
  515.  
  516.         return(ppr);
  517. }
  518.  
  519.  
  520. /* public function
  521.  *
  522.  * PCHAR InsEndChars(PED ped, PPPR pppr, POFFSET pcch)
  523.  *
  524.  * reserves *pcch characters at the end of the piece described by
  525.  * *pppr.  If there isn't enough space at the end of that segment,
  526.  * a new piece is created and linked as next in text order after
  527.  * the provided segment.  The function doesn't guarantee that
  528.  * *pcch characters will be reserved; the amount actually reserved
  529.  * is returned in *pcch.  It is guaranteed that at least one character
  530.  * will be reserved so progress will be made; anything else is
  531.  * considered a failure and the function will return NULL (as well
  532.  * as a zero *pcch).  Since the piece record may change, the
  533.  * function also modifies the pppr to guarantee that the piece
  534.  * out of which text space was allocated is available to the caller.
  535.  */
  536. public PCHAR InsEndChars(PED ped, PPPR pppr, POFFSET pcch)
  537. {
  538.         PCHAR pch;
  539.         PPR ppr2;
  540.         OFFSET cchRes;
  541.  
  542.         cchRes = min((*pppr)->cchUnused, *pcch);
  543.         if (cchRes > 0) {
  544.                 pch = (*pppr)->pchText + (*pppr)->cchText;
  545.                 *pcch = cchRes;
  546.                 Reserve((*pppr),cchRes);
  547.         } else {
  548.                 *pcch = (OFFSET)min((ULONG)*pcch, PIECE_MAXCREATE);
  549.                 ppr2 = CreateSizedPiece(ped, PIECE_MINCREATE, *pcch);
  550.                 if (ppr2 == NULL) {
  551.                         *pcch = 0;
  552.                         pch = NULL;
  553.                 } else {
  554.                         InsertPrAfter((*pppr),ppr2);
  555.                         *pppr = ppr2;
  556.                         pch = ppr2->pchText;
  557.                         *pcch = min(*pcch, ppr2->cchUnused);
  558.                         Reserve(ppr2,*pcch);
  559.                 }
  560.         }
  561.         return(pch);
  562. }
  563.  
  564.  
  565. /* public function
  566.  *
  567.  * PMR InsEndMarker(PED ped, PPR ppr, USHORT fFlags)
  568.  *
  569.  * Inserts a marker piece after the piece ppr, with flags fFlags.
  570.  * Returns a pointer to the marker piece inserted.
  571.  */
  572. public PMR InsEndMarker(PED ped, PPR ppr, USHORT fFlags)
  573. {
  574.         PMR pmr;
  575.  
  576.     pmr = CreateMarkerPiece(ped);
  577.     if (pmr != NULL) {
  578.         pmr->fFlags = fFlags;
  579.         InsertPrAfter(ppr,(PPR)pmr);
  580.     }
  581.     return(pmr);
  582. }
  583.  
  584.  
  585. /* public function
  586.  *
  587.  * PMR InsBeforeMarker(PED ped, PPR ppr, USHORT fFlags)
  588.  *
  589.  * Inserts a marker piece before the piece ppr, with flags fFlags.
  590.  * Returns a pointer to the marker piece inserted.
  591.  */
  592. public PMR InsBeforeMarker(PED ped, PPR ppr, USHORT fFlags)
  593. {
  594.         PMR pmr;
  595.  
  596.     pmr = CreateMarkerPiece(ped);
  597.     if (pmr != NULL) {
  598.         pmr->fFlags = fFlags;
  599.         InsertPrBefore((PPR)pmr,ppr);
  600.     }
  601.     return(pmr);
  602. }
  603.  
  604.  
  605. /* public function
  606.  *
  607.  * PPR DelStartChars(PED ped, PPR ppr, OFFSET cch)
  608.  *
  609.  * Deletes cch characters at the beginning of a piece.
  610.  * Returns pointer to piece record containing text following deletion, or
  611.  * to last piece record in list if deleting last piece in the list.
  612.  * PPR may be invalid after deletion.
  613.  * If cch == 0 on a piece containing text, does nothing.
  614.  */
  615. public PPR DelStartChars(PED ped, PPR ppr, OFFSET cch)
  616. {
  617.         PPR ppr2;
  618.  
  619.         if (cch == ppr->cchText) {
  620.                 ppr2 = (ppr->pprTextNext != NULL)
  621.                                 ? ppr->pprTextNext
  622.                                 : ppr->pprTextPrev;
  623.         } else {
  624.                 if (cch == 0) {
  625.                         return(ppr);
  626.                 }
  627.                 ppr2 = SplitPiece(ped, ppr, cch);
  628.         }
  629.         DestroyPiece(ped, ppr);
  630.         return(ppr2);
  631. }
  632.  
  633.  
  634. /* public function
  635.  *
  636.  * PPR DelEndChars(PED ped, PPR ppr, OFFSET cch)
  637.  *
  638.  * Deletes cch characters from the end of a piece.  Returns piece containing
  639.  * text immediately following deletion, or NULL if no next pieces exist.
  640.  * If cch == 0, gets out as fast as possible.
  641.  * Note that the PPR passed in may be invalid after this function.
  642.  */
  643. public PPR DelEndChars(PED ped, PPR ppr, OFFSET cch)
  644. {
  645.         PPR ppr2;
  646.  
  647.  
  648.         if (ppr->cchText == cch) {
  649.                 ppr2 = ppr->pprTextNext;
  650.                 DestroyPiece(ped,ppr);
  651.                 return(ppr2);
  652.         } else {
  653.                 BubbleUpCheck(ped, ppr, cch);
  654.                 UnReserve(ppr, cch);
  655.                 return(ppr->pprTextNext);
  656.         }
  657. }
  658.  
  659. /* public function
  660.  *
  661.  * PPR DelMarker(PED ped, PMR pmr)
  662.  *
  663.  * Deletes a marker piece.  Returns the next piece in text order if any,
  664.  * or previous piece if no next piece exists.
  665.  */
  666. public PPR DelMarker(PED ped, PMR pmr)
  667. {
  668.     PPR ppr;
  669.  
  670.     ppr = pmr->pr.pprTextNext;
  671.     if (ppr == NULL) {
  672.         ppr = pmr->pr.pprTextPrev;
  673.     }
  674.     DestroyPiece(ped,(PPR)pmr);
  675.     return(ppr);
  676. }
  677.  
  678.  
  679. /* public function
  680.  *
  681.  * PPR SplitPiece(PED ped, PPR ppr, OFFSET cch)
  682.  *
  683.  * splits a piece into two pieces by shortening the existing piece to
  684.  * cch characters; all remaining characters and any free space are
  685.  * given to a new piece.  The new piece is returned from this function.
  686.  * Boundary conditions of cch==0 and cch==size are legal and split
  687.  * off empty pieces.
  688.  * Returns new, right-hand, piece.
  689.  */
  690. public PPR SplitPiece(PED ped, PPR ppr, OFFSET cch)
  691. {
  692.         PPR ppr2;
  693.         OFFSET cchMoved;
  694.  
  695.         ppr2 = CreatePiecerec(ped, ppr->cchUnused+ppr->cchText-cch,
  696.                         ppr->pchText+cch, ppr->psd);
  697.         InsertPrAfterPhys(ppr,ppr2);
  698.         InsertPrAfter(ppr,ppr2);
  699.         cchMoved = ppr->cchText-cch;
  700.         UnReserve(ppr,cchMoved);
  701.         Reserve(ppr2,cchMoved);
  702.         ppr->cchUnused = 0;
  703.         return(ppr2);
  704. }
  705.  
  706.  
  707. /* public function
  708.  *
  709.  * PPR ConditionalSplitPiece(PED ped, PPR ppr, OFFSET cch)
  710.  *
  711.  * if ppr/cch doesn't already point to a split, splits the piece
  712.  * at the appropriate place.  Either way, function returns the piece
  713.  * immediately following the split.  Returns NULL if the ppr/cch point
  714.  * past the end of the text.  If possible, works to minimise proliferation
  715.  * of unneeded pieces by shifting piece boundaries rather than creating a
  716.  * new piece.
  717.  */
  718. public PPR ConditionalSplitPiece(PED ped, PPR ppr, OFFSET cch)
  719. {
  720.         PPR pprD;
  721.  
  722.         if (cch == 0)
  723.                 return(ppr);
  724.  
  725.         if (cch >= ppr->cchText)
  726.                 return(ppr->pprTextNext);
  727.  
  728.         pprD = ppr->pprTextPrev;
  729.  
  730.         if ((ppr->pprPhysPrev == pprD) && (pprD->cchUnused == 0)) {
  731.                 ppr->pchText += cch;
  732.                 ppr->cchText -= cch;
  733.                 pprD->cchText += cch;
  734.                 if (ppr->cchText == 0) {
  735.                         DestroyPiece(ped, ppr);
  736.                         return(pprD->pprTextNext);
  737.                 } else {
  738.                         return(ppr);
  739.                 }
  740.         }
  741.  
  742.         return(SplitPiece(ped,ppr,cch));
  743. }
  744.  
  745. /* public function
  746.  *
  747.  * PPR AppendSegment(PED ped, PPR ppr, SEL sel, OFFSET cch)
  748.  *
  749.  * Given a selector for a segment, and the length of that segment,
  750.  * inserts the segment's text immediately after ppr.  After the
  751.  * append, the segment is assumed to be under the piece manager's
  752.  * control and will be used to store piece text like any other segment.
  753.  */
  754. public PPR AppendSegment(PED ped, PPR ppr, SEL sel, OFFSET cch)
  755. {
  756.         PSEGDESC psd;
  757.         PPR pprNew;
  758.  
  759.         psd = AllocStore(ped, sizeof(SEGDESC));
  760.         if (psd == NULL) {
  761.                 // out of memory error
  762.                 return(NULL);
  763.         }
  764.         psd->cchFree = 0;
  765.         psd->cchLen = cch;
  766.         psd->psdNext = psd->psdPrev = NULL;
  767.  
  768.         pprNew = CreatePiecerec(ped, cch, MAKEP(sel,0), psd);
  769.         InsertPrAfter(ppr, pprNew);
  770.  
  771.         psd->pprPhysLast = ppr;
  772.         psd->sel = sel;
  773.  
  774.         InsertSegAtEnd(ped, psd);
  775.  
  776.         return(pprNew);
  777. }
  778.  
  779. /* public function
  780.  *
  781.  * PPR PprOfOffset(PPR ppr, PIPT poffCh)
  782.  *
  783.  * Given a ppr, scans from the beginning of the pr until the pr
  784.  * *poffCh later has been found.  Returns that ppr, and the offset
  785.  * into that piece.  If an offset that is logically between
  786.  * pieces is specified, then to piece following the ipt is
  787.  * returned, except for the last piece in a list.  If an ipt greater
  788.  * than the length of text is supplied, then the last piece in the
  789.  * list is returned and the offset is truncated to the length
  790.  * of that piece.
  791.  */
  792. public PPR PprOfOffset(PPR ppr, PIPT poffCh)
  793. {
  794.         if (ppr == NULL) {
  795.                 *poffCh = 0;
  796.                 return(NULL);
  797.         }
  798.  
  799.         while ((ppr->pprTextNext != NULL) && (*poffCh >= ppr->cchText)) {
  800.                 *poffCh -= ppr->cchText;
  801.                 ppr = ppr->pprTextNext;
  802.         }
  803.         *poffCh = max((IPT)0,min(*poffCh, (IPT)(ppr->cchText)));
  804.         return(ppr);
  805. }
  806.  
  807.  
  808. /* public function
  809.  *
  810.  * BOOL fIsMarker(PPR ppr)
  811.  *
  812.  * Given a ppr, returns whether it points to a marker or ordinary piece record
  813.  */
  814. public BOOL fIsMarker(PPR ppr)
  815. {
  816.         if (ppr == NULL) {
  817.                 return(FALSE);
  818.         }
  819.  
  820.         return(ppr->pchText == NULL);
  821. }
  822.  
  823. /* public function
  824.  *
  825.  * PPR SkipMarkers(PPR ppr, USHORT fMask)
  826.  *
  827.  * Given a ppr, skips zero or more marker pieces which match the mask
  828.  * to get to the first non-matching or non-marker piece (which may be the
  829.  * value passed in.)  Returns that
  830.  * piece.  'Match' is defined as 'flag bits, when anded with fMask, yield
  831.  * a nonzero value.'
  832.  */
  833. public PPR SkipMarkers(PPR ppr, USHORT fMask)
  834. {
  835.         while (fIsMarker(ppr) &&
  836.             (ppr->pprTextNext != NULL) &&
  837.             (((PMR)ppr)->fFlags & fMask)) {
  838.                 ppr = ppr->pprTextNext;
  839.         }
  840.         return(ppr);
  841. }
  842.  
  843. /* public function
  844.  *
  845.  * PPR SkipBackText(PPR ppr, USHORT fMask)
  846.  *
  847.  * Given a ppr, scans backwards across zero or more pieces to get to the first
  848.  * marker piece (which may be the value passed in) which matches the
  849.  * mask (as in SkipMarkers.)  Returns that piece.
  850.  */
  851. public PPR SkipBackText(PPR ppr, USHORT fMask)
  852. {
  853.         while ((!fIsMarker(ppr) || !(((PMR)ppr)->fFlags & fMask)) &&
  854.             (ppr->pprTextPrev != NULL)) {
  855.                 ppr = ppr->pprTextPrev;
  856.         }
  857.         return(ppr);
  858. }
  859.  
  860.  
  861. /* public function
  862.  *
  863.  * PPR SkipText(PPR ppr, USHORT fMask)
  864.  *
  865.  * Given a ppr, scans forwards across zero or more pieces to get to the first
  866.  * marker piece (which may be the value passed in) which matches the
  867.  * mask (as in SkipMarkers.)  Returns that piece.
  868.  */
  869. public PPR SkipText(PPR ppr, USHORT fMask)
  870. {
  871.         while ((!fIsMarker(ppr) || !(((PMR)ppr)->fFlags & fMask)) &&
  872.             (ppr->pprTextNext != NULL)) {
  873.                 ppr = ppr->pprTextNext;
  874.         }
  875.         return(ppr);
  876. }
  877.  
  878.  
  879. /* public function
  880.  *
  881.  * BOOL LockMarker(PMR pmr, BOOL fLocked)
  882.  *
  883.  * Given a marker piece, locks or unlocks that piece.  Returns the old lock
  884.  * status of the piece.
  885.  */
  886. public BOOL LockMarker(PMR pmr, BOOL fLocked)
  887. {
  888.         BOOL fOld;
  889.  
  890.         fOld = pmr->fLocked;
  891.         pmr->fLocked = fLocked;
  892.         return(fOld);
  893. }
  894.  
  895. /* public function
  896.  *
  897.  * CHAR CharOfPointer(PPR ppr, OFFSET off)
  898.  *
  899.  * Gives character at a given pointer.  Returns \0 if the pointer is invalid.
  900.  */
  901. public CHAR CharOfPointer(PPR ppr, OFFSET off)
  902. {
  903.         return(((off > ppr->cchText) || (off < 0))
  904.                 ? (CHAR)'\0'
  905.                 : ((fIsMarker(ppr))
  906.                         ? (CHAR)'\0'
  907.                         : (*(ppr->pchText+off))));
  908. }
  909.  
  910. /* public function
  911.  *
  912.  * CHAR AdvancePointer(PPPR pppr, POFFSET poff)
  913.  *
  914.  * Advances a ppr/offset pair by one.
  915.  */
  916. public CHAR AdvancePointer(PPPR pppr, POFFSET poff)
  917. {
  918.     (*poff) += 1;
  919.     if (*poff >= (*pppr)->cchText) {
  920.         if ((*pppr)->pprTextNext == NULL) {
  921.             (*poff) -= 1;
  922.             return((CHAR)'\0');
  923.         } else {
  924.             *pppr = (*pppr)->pprTextNext;
  925.             *poff = 0;
  926.             return((fIsMarker(*pppr))?(CHAR)'\0':*((*pppr)->pchText));
  927.         }
  928.     } else
  929.         return(*((*pppr)->pchText + (*poff)));
  930. }
  931.  
  932.  
  933. /* public function
  934.  *
  935.  * CHAR RetreatPointer(PPPR pppr, POFFSET poff)
  936.  *
  937.  * Retracts a ppr/offset pair by one.
  938.  */
  939. public CHAR RetreatPointer(PPPR pppr, POFFSET poff)
  940. {
  941.     if (*poff == 0) {
  942.         *pppr = (*pppr)->pprTextPrev;
  943.         *poff = ((*pppr)->cchText==0) ? 0 : (*pppr)->cchText-1;
  944.     } else {
  945.         (*poff) -= 1;
  946.     }
  947.     return((fIsMarker(*pppr))
  948.             ? (CHAR)'\0'
  949.             : (*((*pppr)->pchText + (*poff))));
  950. }
  951.  
  952.  
  953. /***********************************************************************
  954. * Initialisation
  955. *
  956. * These functions should only be called at startup.  They initialise
  957. * the piece manager.
  958. ***********************************************************************/
  959.  
  960. /* public function
  961.  *
  962.  * PPR PieceInit(PED ped, OFFSET offSize)
  963.  *
  964.  * Initialise piece management
  965.  * Primitive memory manager should be initialised first.
  966.  *
  967.  * Returns the empty piece record created for the empty segment.
  968.  */
  969.  
  970. public PPR PieceInit(PED ped, OFFSET offSize)
  971. {
  972.         ped->psdCurrent = NULL;
  973.         ped->psdLast = NULL;
  974.         CreateSegment(ped,offSize);
  975.         if (ped->psdCurrent == NULL)
  976.                 return NULL;
  977.         else
  978.                 return ped->psdCurrent->pprPhysLast;
  979. }
  980.