home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 March / macformat-022.iso / Shareware City / Developers / src / out-of-phase-102-c / OutOfPhase 1.02 Source / OutOfPhase Folder / Level 1 Extensions 29Sep94 / TextStorage.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-11-23  |  21.8 KB  |  724 lines  |  [TEXT/KAHL]

  1. /* TextStorage.c */
  2. /*****************************************************************************/
  3. /*                                                                           */
  4. /*    System Dependency Library for Building Portable Software               */
  5. /*    Macintosh Version                                                      */
  6. /*    Written by Thomas R. Lawrence, 1993 - 1994.                            */
  7. /*                                                                           */
  8. /*    This file is Public Domain; it may be used for any purpose whatsoever  */
  9. /*    without restriction.                                                   */
  10. /*                                                                           */
  11. /*    This package is distributed in the hope that it will be useful,        */
  12. /*    but WITHOUT ANY WARRANTY; without even the implied warranty of         */
  13. /*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                   */
  14. /*                                                                           */
  15. /*    Thomas R. Lawrence can be reached at tomlaw@world.std.com.             */
  16. /*                                                                           */
  17. /*****************************************************************************/
  18.  
  19. #include "MiscInfo.h"
  20. #include "Audit.h"
  21. #include "Debug.h"
  22. #include "Definitions.h"
  23.  
  24. #include "TextStorage.h"
  25. #include "Memory.h"
  26. #include "DataMunging.h"
  27. #include "Array.h"
  28. #include "Files.h"
  29. #include "BufferedFileOutput.h"
  30.  
  31.  
  32. /* Interesting note:  Any text document automatically has at least one line:  */
  33. /* an empty document has one line with zero characters on it.  However, this */
  34. /* data structure allows zero lines to be stored.  To get around this problem, */
  35. /* a blank line is automatically allocated.  Unless I've overlooked something, */
  36. /* none of the cut and paste type routines should ever result in a structure */
  37. /* containing fewer than one line, but this is something to watch out for. */
  38.  
  39.  
  40. struct TextStorageRec
  41.     {
  42.         ArrayRec*                LineMap;
  43.         MyBoolean                DataHasChanged;
  44.     };
  45.  
  46.  
  47. /* allocate a new structure for storing text type data */
  48. TextStorageRec*            NewTextStorage(void)
  49.     {
  50.         TextStorageRec*            Storage;
  51.         char*                                Thang;
  52.  
  53.         Storage = (TextStorageRec*)AllocPtrCanFail(
  54.             sizeof(TextStorageRec),"TextStorageRec");
  55.         if (Storage == NIL)
  56.             {
  57.              MemOut1:
  58.                 return NIL;
  59.             }
  60.         Storage->LineMap = NewArray();
  61.         Thang = AllocPtrCanFail(0,"TextLine");
  62.         if (Thang == NIL)
  63.             {
  64.                 /* things are in sad shape if this failed */
  65.              MemOut2:
  66.                 DisposeArray(Storage->LineMap);
  67.                 goto MemOut1;
  68.             }
  69.         if (!ArrayAppendElement(Storage->LineMap,Thang))
  70.             {
  71.                 ReleasePtr(Thang);
  72.                 goto MemOut2;
  73.             }
  74.         Storage->DataHasChanged = False;
  75.         return Storage;
  76.     }
  77.  
  78.  
  79. /* dispose the text storage structure and any data it contains */
  80. void                                DisposeTextStorage(TextStorageRec* Storage)
  81.     {
  82.         long                Limit;
  83.         long                Scan;
  84.  
  85.         CheckPtrExistence(Storage);
  86.         Limit = ArrayGetLength(Storage->LineMap);
  87.         for (Scan = 0; Scan < Limit; Scan += 1)
  88.             {
  89.                 ReleasePtr((char*)ArrayGetElement(Storage->LineMap,Scan));
  90.             }
  91.         DisposeArray(Storage->LineMap);
  92.         ReleasePtr((char*)Storage);
  93.     }
  94.  
  95.  
  96. /* find out how many lines the text storage object contains */
  97. long                                TextStorageGetLineCount(TextStorageRec* Storage)
  98.     {
  99.         CheckPtrExistence(Storage);
  100.         ERROR(ArrayGetLength(Storage->LineMap) < 1,
  101.             PRERR(ForceAbort,"TextStorageGetLineCount:  0 lines"));
  102.         return ArrayGetLength(Storage->LineMap);
  103.     }
  104.  
  105.  
  106. /* get a copy of the specified line */
  107. char*                                TextStorageGetLineCopy(TextStorageRec* Storage, long LineIndex)
  108.     {
  109.         char*                Copy;
  110.  
  111.         CheckPtrExistence(Storage);
  112.         ERROR((LineIndex < 0) || (LineIndex >= TextStorageGetLineCount(Storage)),
  113.             PRERR(ForceAbort,"TextStorageGetLine:  Line index out of range"));
  114.         Copy = CopyPtr((char*)ArrayGetElement(Storage->LineMap,LineIndex));
  115.         if (Copy != NIL)
  116.             {
  117.                 SetTag(Copy,"TextLineCopy");
  118.             }
  119.         return Copy;
  120.     }
  121.  
  122.  
  123. /* get a pointer to the actual line as stored in the array.  this line should */
  124. /* not be modified in any way. */
  125. char*                                TextStorageGetActualLine(TextStorageRec* Storage, long LineIndex)
  126.     {
  127.         CheckPtrExistence(Storage);
  128.         ERROR((LineIndex < 0) || (LineIndex >= TextStorageGetLineCount(Storage)),
  129.             PRERR(ForceAbort,"TextStorageGetLine:  Line index out of range"));
  130.         return (char*)ArrayGetElement(Storage->LineMap,LineIndex);
  131.     }
  132.  
  133.  
  134. /* get the length of the specified line */
  135. long                                TextStorageGetLineLength(TextStorageRec* Storage, long LineIndex)
  136.     {
  137.         CheckPtrExistence(Storage);
  138.         ERROR((LineIndex < 0) || (LineIndex >= TextStorageGetLineCount(Storage)),
  139.             PRERR(ForceAbort,"TextStorageGetLineLength:  Line index out of range"));
  140.         return PtrSize((char*)ArrayGetElement(Storage->LineMap,LineIndex));
  141.     }
  142.  
  143.  
  144. /* put a new line in the text storage object.  the original contents of the */
  145. /* specified line are deleted.  returns False if it couldn't be completed. */
  146. /* (it could fail since a copy of the line is made) */
  147. MyBoolean                        TextStorageChangeLine(TextStorageRec* Storage, long LineIndex,
  148.                                             char* NewLine)
  149.     {
  150.         char*                    Copy;
  151.  
  152.         CheckPtrExistence(Storage);
  153.         ERROR((LineIndex < 0) || (LineIndex >= TextStorageGetLineCount(Storage)),
  154.             PRERR(ForceAbort,"TextStorageChangeLine:  Line index out of range"));
  155.         Copy = CopyPtr(NewLine);
  156.         if (Copy == NIL)
  157.             {
  158.                 return False;
  159.             }
  160.         SetTag(Copy,"TextLine");
  161.         /* don't commit unless we know the operation will succeed */
  162.         ReleasePtr((char*)ArrayGetElement(Storage->LineMap,LineIndex));
  163.         ArraySetElement(Storage->LineMap,Copy,LineIndex);
  164.         Storage->DataHasChanged = True;
  165.         return True;
  166.     }
  167.  
  168.  
  169. /* insert a new empty line at the specified position */
  170. MyBoolean                        TextStorageInsertLine(TextStorageRec* Storage, long LineIndex)
  171.     {
  172.         char*                    Empty;
  173.  
  174.         CheckPtrExistence(Storage);
  175.         ERROR((LineIndex < 0) || (LineIndex >/*NB*/ TextStorageGetLineCount(Storage)),
  176.             PRERR(ForceAbort,"TextStorageInsertLine:  Line index out of range"));
  177.         Empty = AllocPtrCanFail(0,"TextLine");
  178.         if (Empty == NIL)
  179.             {
  180.              FailurePoint1:
  181.                 return False;
  182.             }
  183.         if (!ArrayInsertElement(Storage->LineMap,Empty,LineIndex))
  184.             {
  185.              FailurePoint2:
  186.                 ReleasePtr(Empty);
  187.                 goto FailurePoint1;
  188.             }
  189.         Storage->DataHasChanged = True;
  190.         return True;
  191.     }
  192.  
  193.  
  194. /* delete the line at the specified position */
  195. void                                TextStorageDeleteLine(TextStorageRec* Storage, long LineIndex)
  196.     {
  197.         CheckPtrExistence(Storage);
  198.         ERROR((LineIndex < 0) || (LineIndex >= TextStorageGetLineCount(Storage)),
  199.             PRERR(ForceAbort,"TextStorageDeleteLine:  Line index out of range"));
  200.         ReleasePtr((char*)ArrayGetElement(Storage->LineMap,LineIndex));
  201.         ArrayDeleteElement(Storage->LineMap,LineIndex);
  202.         Storage->DataHasChanged = True;
  203.     }
  204.  
  205.  
  206. /* break the line at the specified character index.  this is used for inserting */
  207. /* carriage returns. */
  208. MyBoolean                        TextStorageBreakLine(TextStorageRec* Storage, long LineIndex,
  209.                                             long CharIndex)
  210.     {
  211.         char*                OldLine;
  212.         char*                FirstHalf;
  213.         long                OldLength;
  214.         char*                SecondHalf;
  215.  
  216.         CheckPtrExistence(Storage);
  217.         ERROR((LineIndex < 0) || (LineIndex >= TextStorageGetLineCount(Storage)),
  218.             PRERR(ForceAbort,"TextStorageBreakLine:  Line index out of range"));
  219.         OldLine = (char*)ArrayGetElement(Storage->LineMap,LineIndex);
  220.         OldLength = PtrSize(OldLine);
  221.         ERROR((CharIndex < 0) || (CharIndex > OldLength),
  222.             PRERR(ForceAbort,"TextStorageBreakLine:  Character break index out of range"));
  223.         FirstHalf = MidBlockCopy(OldLine,0,CharIndex);
  224.         if (FirstHalf == NIL)
  225.             {
  226.              FailurePoint1:
  227.                 return False;
  228.             }
  229.         SetTag(FirstHalf,"TextLine");
  230.         SecondHalf = MidBlockCopy(OldLine,CharIndex,OldLength - CharIndex);
  231.         if (SecondHalf == NIL)
  232.             {
  233.              FailurePoint2:
  234.                 ReleasePtr(FirstHalf);
  235.                 goto FailurePoint1;
  236.             }
  237.         SetTag(SecondHalf,"TextLine");
  238.         if (!ArrayInsertElement(Storage->LineMap,SecondHalf,LineIndex + 1))
  239.             {
  240.              FailurePoint3:
  241.                 ReleasePtr(SecondHalf);
  242.                 goto FailurePoint2;
  243.             }
  244.         ReleasePtr(OldLine);
  245.         /* secondhalf already inserted; need to update firsthalf */
  246.         ArraySetElement(Storage->LineMap,FirstHalf,LineIndex);
  247.         Storage->DataHasChanged = True;
  248.         return True;
  249.     }
  250.  
  251.  
  252. /* replace the line and the line after it with a single line containing the */
  253. /* second line concatenated onto the end of the first line */
  254. MyBoolean                        TextStorageFoldLines(TextStorageRec* Storage, long LineIndex)
  255.     {
  256.         char*                FirstLine;
  257.         char*                SecondLine;
  258.         char*                Resultant;
  259.  
  260.         CheckPtrExistence(Storage);
  261.         ERROR((LineIndex < 0) || (LineIndex + 1/*NB*/ >= TextStorageGetLineCount(Storage)),
  262.             PRERR(ForceAbort,"TextStorageFoldLines:  Line index out of range"));
  263.         FirstLine = (char*)ArrayGetElement(Storage->LineMap,LineIndex);
  264.         SecondLine = (char*)ArrayGetElement(Storage->LineMap,LineIndex + 1);
  265.         Resultant = ConcatBlockCopy(FirstLine,SecondLine);
  266.         if (Resultant == NIL)
  267.             {
  268.                 return False;
  269.             }
  270.         SetTag(Resultant,"TextLine");
  271.         ArrayDeleteElement(Storage->LineMap,LineIndex + 1);
  272.         ReleasePtr(FirstLine);
  273.         ReleasePtr(SecondLine);
  274.         /* second line already deleted; update first line */
  275.         ArraySetElement(Storage->LineMap,Resultant,LineIndex);
  276.         Storage->DataHasChanged = True;
  277.         return True;
  278.     }
  279.  
  280.  
  281. /* extract part of the stored data in the form of another text storage object */
  282. TextStorageRec*            TextStorageExtractSection(TextStorageRec* Storage,
  283.                                             long StartLine, long StartChar, long EndLine, long EndChar)
  284.     {
  285.         TextStorageRec*        Copy;
  286.  
  287.         CheckPtrExistence(Storage);
  288.         ERROR((StartLine < 0) || (StartLine >= TextStorageGetLineCount(Storage))
  289.             || (EndLine < 0) || (EndLine >= TextStorageGetLineCount(Storage)),
  290.             PRERR(ForceAbort,"TextStorageExtractSection:  Line index out of range"));
  291.         ERROR((StartLine > EndLine) || ((StartLine == EndLine) && (StartChar > EndChar)),
  292.             PRERR(ForceAbort,"TextStorageExtractSection:  Inconsistent range specified"));
  293.         ERROR((StartChar < 0) || (StartChar > TextStorageGetLineLength(Storage,StartLine))
  294.             || (EndChar < 0) || (EndChar > TextStorageGetLineLength(Storage,EndLine)),
  295.             PRERR(ForceAbort,
  296.             "TextStorageExtractSection:  Character ranges for lines exceeded"));
  297.         Copy = NewTextStorage();
  298.         if (Copy == NIL)
  299.             {
  300.              FailurePoint1:
  301.                 return NIL;
  302.             }
  303.         if (StartLine == EndLine)
  304.             {
  305.                 char*                            SubsetCopy;
  306.                 MyBoolean                    Flag;
  307.                 char*                            Line;
  308.  
  309.                 /* special case */
  310.                 Line = (char*)ArrayGetElement(Storage->LineMap,StartLine);
  311.                 SubsetCopy = MidBlockCopy(Line,StartChar,EndChar - StartChar);
  312.                 if (SubsetCopy == NIL)
  313.                     {
  314.                      FailurePoint2:
  315.                         DisposeTextStorage(Copy);
  316.                         goto FailurePoint1;
  317.                     }
  318.                 SetTag(SubsetCopy,"TextLine");
  319.                 /* No need to insert line into Copy because new TextStorage's always */
  320.                 /* contain one line */
  321.                 Flag = TextStorageChangeLine(Copy,0,SubsetCopy);
  322.                 ReleasePtr(SubsetCopy);
  323.                 if (!Flag)
  324.                     {
  325.                         goto FailurePoint2;
  326.                     }
  327.             }
  328.          else
  329.             {
  330.                 long                            LineScan;
  331.                 long                            TargetLineTracker;
  332.                 char*                            Line;
  333.                 char*                            SubsetCopy;
  334.                 MyBoolean                    Flag;
  335.  
  336.                 TargetLineTracker = 0;
  337.                 /* munch start line */
  338.                 Line = (char*)ArrayGetElement(Storage->LineMap,StartLine);
  339.                 SubsetCopy = MidBlockCopy(Line,StartChar,PtrSize(Line) - StartChar);
  340.                 if (SubsetCopy == NIL)
  341.                     {
  342.                         goto FailurePoint2;
  343.                     }
  344.                 /* No need to insert first line into Copy because new TextStorage's always */
  345.                 /* contain one line */
  346.                 Flag = TextStorageChangeLine(Copy,TargetLineTracker,SubsetCopy);
  347.                 ReleasePtr(SubsetCopy);
  348.                 if (!Flag)
  349.                     {
  350.                         goto FailurePoint2;
  351.                     }
  352.                 TargetLineTracker += 1;
  353.                 /* munch middle lines (if any) */
  354.                 for (LineScan = StartLine + 1; LineScan <= EndLine - 1; LineScan += 1)
  355.                     {
  356.                         if (!TextStorageInsertLine(Copy,TargetLineTracker))
  357.                             {
  358.                                 goto FailurePoint2;
  359.                             }
  360.                         if (!TextStorageChangeLine(Copy,TargetLineTracker,
  361.                             (char*)ArrayGetElement(Storage->LineMap,LineScan)))
  362.                             {
  363.                                 goto FailurePoint2;
  364.                             }
  365.                         TargetLineTracker += 1;
  366.                     }
  367.                 /* munch last line */
  368.                 Line = (char*)ArrayGetElement(Storage->LineMap,EndLine);
  369.                 SubsetCopy = MidBlockCopy(Line,0,EndChar);
  370.                 if (SubsetCopy == NIL)
  371.                     {
  372.                         goto FailurePoint2;
  373.                     }
  374.                 if (!TextStorageInsertLine(Copy,TargetLineTracker))
  375.                     {
  376.                         ReleasePtr(SubsetCopy);
  377.                         goto FailurePoint2;
  378.                     }
  379.                 Flag = TextStorageChangeLine(Copy,TargetLineTracker,SubsetCopy);
  380.                 ReleasePtr(SubsetCopy);
  381.                 if (!Flag)
  382.                     {
  383.                         goto FailurePoint2;
  384.                     }
  385.                 /* TargetLineTracker += 1; -- not needed at end */
  386.             }
  387.         return Copy;
  388.     }
  389.  
  390.  
  391. /* delete the specified range of data from the storage.  returns False if it */
  392. /* failed.  if this routine fails, it may have left the task partially finished */
  393. MyBoolean                        TextStorageDeleteSection(TextStorageRec* Storage,
  394.                                             long StartLine, long StartChar, long EndLine, long EndChar)
  395.     {
  396.         long                            LineScan;
  397.         char*                            Line;
  398.  
  399.         CheckPtrExistence(Storage);
  400.         ERROR((StartLine < 0) || (StartLine >= TextStorageGetLineCount(Storage))
  401.             || (EndLine < 0) || (EndLine >= TextStorageGetLineCount(Storage)),
  402.             PRERR(ForceAbort,"TextStorageDeleteSection:  Line index out of range"));
  403.         ERROR((StartLine > EndLine) || ((StartLine == EndLine) && (StartChar > EndChar)),
  404.             PRERR(ForceAbort,"TextStorageDeleteSection:  Inconsistent range specified"));
  405.         ERROR((StartChar < 0) || (StartChar > TextStorageGetLineLength(Storage,StartLine))
  406.             || (EndChar < 0) || (EndChar > TextStorageGetLineLength(Storage,EndLine)),
  407.             PRERR(ForceAbort,
  408.             "TextStorageDeleteSection:  Character ranges for lines exceeded"));
  409.         Storage->DataHasChanged = True;
  410.         if (StartLine == EndLine)
  411.             {
  412.                 char*                            LineCopy;
  413.  
  414.                 /* special case */
  415.                 Line = (char*)ArrayGetElement(Storage->LineMap,StartLine);
  416.                 LineCopy = RemoveBlockFromBlockCopy(Line,StartChar,EndChar - StartChar);
  417.                 if (LineCopy == NIL)
  418.                     {
  419.                         return False;
  420.                     }
  421.                 SetTag(LineCopy,"TextLine");
  422.                 ReleasePtr(Line);
  423.                 ArraySetElement(Storage->LineMap,LineCopy,StartLine);
  424.             }
  425.          else
  426.             {
  427.                 char*                            LineCopy;
  428.  
  429.                 /* munch start line */
  430.                 Line = (char*)ArrayGetElement(Storage->LineMap,StartLine);
  431.                 LineCopy = RemoveBlockFromBlockCopy(Line,StartChar,
  432.                     PtrSize(Line) - StartChar);
  433.                 if (LineCopy == NIL)
  434.                     {
  435.                         return False;
  436.                     }
  437.                 SetTag(LineCopy,"TextLine");
  438.                 ReleasePtr(Line);
  439.                 ArraySetElement(Storage->LineMap,LineCopy,StartLine);
  440.                 /* munch middle lines (if any) */
  441.                 for (LineScan = StartLine + 1; LineScan <= EndLine - 1; LineScan += 1)
  442.                     {
  443.                         TextStorageDeleteLine(Storage,StartLine + 1);/* keep deleting line after */
  444.                     }
  445.                 /* munch last line */
  446.                 Line = (char*)ArrayGetElement(Storage->LineMap,StartLine + 1); /* line after */
  447.                 LineCopy = RemoveBlockFromBlockCopy(Line,0,EndChar);
  448.                 if (LineCopy == NIL)
  449.                     {
  450.                         return False;
  451.                     }
  452.                 SetTag(LineCopy,"TextLine");
  453.                 ReleasePtr(Line);
  454.                 ArraySetElement(Storage->LineMap,LineCopy,StartLine + 1);
  455.                 /* finally, fold the two remaining lines */
  456.                 if (!TextStorageFoldLines(Storage,StartLine))
  457.                     {
  458.                         return False;
  459.                     }
  460.             }
  461.         return True;
  462.     }
  463.  
  464.  
  465. /* insert a storage block at the specified position into this storage block. */
  466. /* returns False if it failed.  if it fails, then it may have actually inserted */
  467. /* some of the data into the storage record */
  468. /* special note:  there are (number of lines) - 1 line breaks */
  469. MyBoolean                        TextStorageInsertSection(TextStorageRec* Storage,
  470.                                             long WhereLine, long WhereChar, TextStorageRec* Stuff)
  471.     {
  472.         long                            TotalNumLines;
  473.         long                            Scan;
  474.  
  475.         CheckPtrExistence(Storage);
  476.         ERROR((WhereLine < 0) || (WhereLine >= TextStorageGetLineCount(Storage)),
  477.             PRERR(ForceAbort,"TextStorageInsertSection:  Line position out of range"));
  478.         ERROR((WhereChar < 0) || (WhereChar > TextStorageGetLineLength(Storage,WhereLine)),
  479.             PRERR(ForceAbort,"TextStorageInsertSection:  Character position out of range"));
  480.         /* first, we break the line we're inserting into, to make 2 lines which */
  481.         /* we can insert between */
  482.         Storage->DataHasChanged = True;
  483.         if (!TextStorageBreakLine(Storage,WhereLine,WhereChar))
  484.             {
  485.                 return False;
  486.             }
  487.         TotalNumLines = TextStorageGetLineCount(Stuff);
  488.         for (Scan = 0; Scan < TotalNumLines; Scan += 1)
  489.             {
  490.                 char*                            LineCopy;
  491.  
  492.                 /* for each line, insert a copy into ourself */
  493.                 LineCopy = CopyPtr((char*)ArrayGetElement(Stuff->LineMap,Scan));
  494.                 if (LineCopy == NIL)
  495.                     {
  496.                      FailurePoint1:
  497.                         return False;
  498.                     }
  499.                 if (!ArrayInsertElement(Storage->LineMap,LineCopy,WhereLine + 1 + Scan))
  500.                     {
  501.                         ReleasePtr(LineCopy);
  502.                         goto FailurePoint1;
  503.                     }
  504.             }
  505.         /* now we just have to fold the first and last lines back together */
  506.         if (!TextStorageFoldLines(Storage,WhereLine + TotalNumLines))
  507.             {
  508.                 return False;
  509.             }
  510.         if (!TextStorageFoldLines(Storage,WhereLine))
  511.             {
  512.                 return False;
  513.             }
  514.         return True;
  515.     }
  516.  
  517.  
  518. /* if the end of line sequence is of the specified length, then calculate how */
  519. /* many characters a packed buffer of text would contain */
  520. long                                TextStorageTotalNumChars(TextStorageRec* Storage, long EOLNSize)
  521.     {
  522.         long                Accumulator;
  523.         long                LineScan;
  524.         long                Limit;
  525.  
  526.         CheckPtrExistence(Storage);
  527.         Accumulator = 0;
  528.         Limit = TextStorageGetLineCount(Storage);
  529.         for (LineScan = 0; LineScan < Limit; LineScan += 1)
  530.             {
  531.                 Accumulator += TextStorageGetLineLength(Storage,LineScan);
  532.                 if (LineScan != Limit - 1)
  533.                     {
  534.                         /* all but last line have an eoln marker */
  535.                         Accumulator += EOLNSize;
  536.                     }
  537.             }
  538.         return Accumulator;
  539.     }
  540.  
  541.  
  542. /* create a packed buffer of lines separated by the specified end of line sequence. */
  543. /* the end of line sequence is null terminated */
  544. char*                                TextStorageMakeRawBuffer(TextStorageRec* Storage, char* EOLNChar)
  545.     {
  546.         long                            TotalNumChars;
  547.         char*                            Buffer;
  548.         long                            Index;
  549.         long                            LineScan;
  550.         long                            Limit;
  551.         char*                            Line;
  552.         long                            LineLength;
  553.         long                            EOLNLength;
  554.  
  555.         CheckPtrExistence(Storage);
  556.         EOLNLength = StrLen(EOLNChar);
  557.         TotalNumChars = TextStorageTotalNumChars(Storage,EOLNLength);
  558.         Buffer = AllocPtrCanFail(TotalNumChars,"TextStorageRawBuffer");
  559.         if (Buffer == NIL)
  560.             {
  561.                 return NIL;
  562.             }
  563.         Index = 0;
  564.         Limit = TextStorageGetLineCount(Storage);
  565.         for (LineScan = 0; LineScan < Limit; LineScan += 1)
  566.             {
  567.                 Line = TextStorageGetActualLine(Storage,LineScan);
  568.                 LineLength = PtrSize(Line);
  569.                 PRNGCHK(Buffer,&(Buffer[Index]),LineLength);
  570.                 CopyData(Line,&(Buffer[Index]),LineLength);
  571.                 Index += LineLength;
  572.                 if (LineScan != Limit - 1)
  573.                     {
  574.                         /* all but last line get eoln */
  575.                         PRNGCHK(Buffer,&(Buffer[Index]),EOLNLength);
  576.                         CopyData(EOLNChar,&(Buffer[Index]),EOLNLength);
  577.                         Index += EOLNLength;
  578.                     }
  579.             }
  580.         return Buffer;
  581.     }
  582.  
  583.  
  584. /* decode the packed buffer of lines and create a text storage record from it. */
  585. TextStorageRec*            TextStorageFromRawBuffer(char* Buffer, char* EOLNChar)
  586.     {
  587.         TextStorageRec*        Storage;
  588.         long                            LineTracking;
  589.         long                            Index;
  590.         long                            BufferSize;
  591.         long                            CurrentLineLength;
  592.         long                            EOLNLength;
  593.         char*                            TempLine;
  594.         MyBoolean                    LastLineEndedWithCR;
  595.  
  596.         Storage = NewTextStorage();
  597.         if (Storage == NIL)
  598.             {
  599.              FailurePoint1:
  600.                 return NIL;
  601.             }
  602.         BufferSize = PtrSize(Buffer);
  603.         EOLNLength = StrLen(EOLNChar);
  604.         Index = 0;
  605.         LineTracking = 0;
  606.         LastLineEndedWithCR = False;
  607.         while (Index < BufferSize)
  608.             {
  609.                 CurrentLineLength = 0;
  610.                 while ((CurrentLineLength + Index < BufferSize)
  611.                     && ((EOLNLength == 0) || !MemEqu(EOLNChar,
  612.                     &(Buffer[CurrentLineLength + Index]),EOLNLength)))
  613.                     {
  614.                         CurrentLineLength += 1;
  615.                     }
  616.                 if (CurrentLineLength + Index < BufferSize)
  617.                     {
  618.                         LastLineEndedWithCR = (EOLNLength != 0) && MemEqu(EOLNChar,
  619.                             &(Buffer[CurrentLineLength + Index]),EOLNLength);
  620.                     }
  621.                 TempLine = BlockFromRaw(&(Buffer[Index]),CurrentLineLength);
  622.                 if (TempLine == NIL)
  623.                     {
  624.                      FailurePoint2:
  625.                         DisposeTextStorage(Storage);
  626.                         goto FailurePoint1;
  627.                     }
  628.                 if (LineTracking != 0)
  629.                     {
  630.                         /* if it isn't the first line, then we need to append a line */
  631.                         if (!TextStorageInsertLine(Storage,LineTracking))
  632.                             {
  633.                              FailurePoint3:
  634.                                 ReleasePtr(TempLine);
  635.                                 goto FailurePoint2;
  636.                             }
  637.                     }
  638.                 /* store the line in */
  639.                 if (!TextStorageChangeLine(Storage,LineTracking,TempLine))
  640.                     {
  641.                         goto FailurePoint3;
  642.                     }
  643.                 ReleasePtr(TempLine);
  644.                 Index = Index + CurrentLineLength + EOLNLength;
  645.                 LineTracking += 1;
  646.             }
  647.         /* if the block ended with a carriage return, then add a blank line on the end */
  648.         if (LastLineEndedWithCR)
  649.             {
  650.                 if (!TextStorageInsertLine(Storage,LineTracking))
  651.                     {
  652.                         goto FailurePoint2;
  653.                     }
  654.             }
  655.         return Storage;
  656.     }
  657.  
  658.  
  659. /* find out if the data has been changed since the last call to */
  660. /* TextStorageDataIsUpToDate */
  661. MyBoolean                        TextStorageHasDataChanged(TextStorageRec* Storage)
  662.     {
  663.         CheckPtrExistence(Storage);
  664.         return Storage->DataHasChanged;
  665.     }
  666.  
  667.  
  668. /* indicate that any changes in the data have been recognized */
  669. void                                TextStorageDataIsUpToDate(TextStorageRec* Storage)
  670.     {
  671.         CheckPtrExistence(Storage);
  672.         Storage->DataHasChanged = False;
  673.     }
  674.  
  675.  
  676. /* write the entire buffer to a file using the specified end of line sequence. */
  677. /* returns True if successful */
  678. MyBoolean                        TextStorageWriteDataToFile(TextStorageRec* Storage,
  679.                                             FileType* FileRefNum, char* EOLN)
  680.     {
  681.         BufferedOutputRec*    Output;
  682.         long                                Limit;
  683.         long                                Scan;
  684.         long                                EOLNLength;
  685.         MyBoolean                        Success;
  686.  
  687.         CheckPtrExistence(Storage);
  688.         CheckPtrExistence(FileRefNum);
  689.         EOLNLength = StrLen(EOLN);
  690.         Output = NewBufferedOutput(FileRefNum);
  691.         Success = False;
  692.         if (Output != NIL)
  693.             {
  694.                 Success = True;
  695.                 Limit = ArrayGetLength(Storage->LineMap);
  696.                 for (Scan = 0; (Scan < Limit) && Success; Scan += 1)
  697.                     {
  698.                         char*                            DataTemp;
  699.  
  700.                         DataTemp = (char*)ArrayGetElement(Storage->LineMap,Scan);
  701.                         CheckPtrExistence(DataTemp);
  702.                         if (!WriteBufferedOutput(Output,PtrSize(DataTemp),DataTemp))
  703.                             {
  704.                                 Success = False;
  705.                             }
  706.                          else
  707.                             {
  708.                                 if (Scan != Limit - 1)
  709.                                     {
  710.                                         if (!WriteBufferedOutput(Output,EOLNLength,EOLN))
  711.                                             {
  712.                                                 Success = False;
  713.                                             }
  714.                                     }
  715.                             }
  716.                     }
  717.                 if (!EndBufferedOutput(Output))
  718.                     {
  719.                         Success = False;
  720.                     }
  721.             }
  722.         return Success;
  723.     }
  724.