home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Source Code / Pascal / Snippets / StringManipulators / StringManipulators.p < prev   
Encoding:
Text File  |  1995-10-29  |  13.3 KB  |  411 lines  |  [TEXT/CWIE]

  1. {     Version History: First released without a version number. This is the       }
  2. {    second release (STR# Manipulators #2). It fixes a bug in the function         }
  3. {    SetIndString wherein I forgot to reset a pointer after resizing the         }
  4. {    resource handle. This can result in the pointer no longer pointing in the     }
  5. {     proper place within the resource should memory be moved during the             }
  6. {    SetHandleSize call. My apologies if this has caused anyone any trouble.        }
  7. {                                                                                }
  8. {                                                                                }
  9. {                             ABOUT STRING MANIPULATORS                            }                
  10. {                                                                                }
  11. {     The following Pascal routines were written to complement GetIndString. They    }
  12. {    allow the user to add, delete or change individual strings in a string list    }
  13. {    resource (STR#). A fourth routine returns the number of strings in the list.}
  14. {                                                                                }
  15. {     The following routines, DeleteIndString, GetNumStrings, InsertIndString,     }
  16. {    and SetIndString, are copyrighted 1995 by David B. Zwiefelhofer. Feel free     }
  17. {    to use them in your code without giving credit. If you release source code     }
  18. {    incorporating any of these routines please give me credit.                    }
  19. {                                                                                }
  20. {    SetIndString is based on (a rough translation of) a C routine of the same     }
  21. {    name that I saw in one of the comp.sys.mac.programmer newsgroups.             } 
  22. {    Unfortunately, I've lost the original author's name and so I can't credit      }
  23. {    him. If you are the original author, please contact me so that I can give     }
  24. {    you proper credit.                                                            }
  25. {                                                                                }
  26. {    No warranty is made as to the fitness of this code for any use whatsoever.    }
  27. {    The user assumes full responsibility for the use of this code.                }
  28. {                                                                                }
  29. {    SetIndString changes an individual string in a string list resource.        }
  30. {    GetNumStrings returns the number of strings in a string list resource.        }
  31. {    InsertIndString inserts a new string into a string list resource.            }
  32. {    DeleteString removes a string from a string list resource.                    }
  33. {                                                                                }
  34. {    Please direct any comments, questions or bug reports to:                    }
  35. {    subversive@aol.com (preferred) or                                             }
  36. {    davez@mailbag.com.                                                            }
  37.  
  38. unit StringManipulators;
  39. interface
  40.     uses
  41.         Globals;
  42.  
  43.     function GetNumStrings(resID: integer): integer;
  44.  
  45.     function DeleteIndString(resID, strIndex: integer): OSErr;
  46.  
  47.     function SetIndString(theStr: Str255; resID, strIndex: integer): OSErr;
  48.  
  49.     function InsertIndString(theStr: Str255; resID, strIndex: integer): OSErr;
  50.  
  51. implementation
  52.  
  53. {*********************************BlockMoveData**************************************}
  54.  
  55.     procedure BlockMoveData (srcPtr: univ Ptr; destPtr: univ Ptr; byteCount: Size);
  56.     inline
  57.         $201F, $225F, $205F, $A22E;
  58.  
  59.  
  60.  
  61.  
  62. {*********************************GetNumStrings**************************************}
  63. {    Returns the number of strings in a string list (STR#) resource. The parameters         }
  64. {    are: resID is the STR# resource ID.                                                 }
  65.     function GetNumStrings(resID: integer): integer;
  66.         var
  67.            theRes: Handle;          { handle pointing to STR# resource             }
  68.            howMany:integer;            { number of strings in STR# resource         }
  69.         label
  70.             100;
  71.     begin
  72.         howMany := 0;
  73.         
  74.            { make sure resource exists }
  75.         theRes := GetResource(kStringListType, resID);
  76.            if (theRes = nil) then
  77.                goto 100;
  78.  
  79.            HLockHi(theRes);
  80.            HNoPurge(theRes);
  81.  
  82.            { get # of strings in STR# }
  83.            BlockMoveData(theRes^, @howMany, sizeof(SInt16));
  84.  
  85.         ReleaseResource(theRes);
  86. 100:
  87.            GetNumStrings := howMany;
  88.     end;
  89.  
  90.  
  91.  
  92. {*********************************DeleteIndString**************************************}
  93. {    Deletes an individual string in a string list (STR#) resource. The parameters         }
  94. {    are: resID is the STR# resource ID, strIndex is the number of the individual         }
  95. {    string to be deleted.                                                                 }
  96.     function DeleteIndString(resID, strIndex: integer): OSErr;
  97.         var
  98.            theRes: Handle;          { handle pointing to STR# resource             }
  99.            numStrings,                 { number of strings in STR#                 }
  100.            ourString: integer;      { counter to index up to strIndex             }
  101.            resStr: StringPtr;          { string pointer to STR# string to delete     }
  102.            oldSize,                    { size of STR# resource before call         }
  103.            newSize: longint;        { size of STR# resource after call             }
  104.            offset: UInt32;            { resource offset to str to delete             }
  105.            theErr: OSErr;            { error code                                }
  106.            srcPtr, dstPtr: Ptr;        { source and destination pointers and …        }
  107.            byteCount:integer;        { amount to copy for BlockMoveData call        }
  108.         const
  109.             kStringOutOfRange = -100;
  110.         label
  111.             100;
  112.     begin
  113.            { make sure string exists }
  114.         if strIndex < 1 then
  115.         begin
  116.             theErr := kStringOutOfRange;
  117.             goto 100;
  118.         end;
  119.         
  120.            { make sure resource exists }
  121.         theRes := GetResource(kStringListType, resID);
  122.            if (theRes = nil) then
  123.            begin
  124.                theErr := ResError;
  125.                goto 100; 
  126.            end;
  127.  
  128.            HLockHi(theRes);
  129.            HNoPurge(theRes);
  130.  
  131.            { get # of strings in STR# }
  132.  
  133.            BlockMoveData(theRes^, @numStrings, sizeof(SInt16));
  134.            if (strIndex > numStrings) then
  135.            begin
  136.                theErr := kStringOutOfRange;
  137.                goto 100;
  138.            end;
  139.  
  140.            { get a pointer to the string to delete }
  141.         
  142.            offset := sizeof(SInt16);
  143.            resStr := StringPtr(ORD4(theRes^) + sizeof(SInt16)); { pointer to the first string }
  144.            for ourString := 1 to strIndex - 1 do
  145.            begin
  146.                offset := offset + 1 + ORD4(resStr^[0]); { offset is distant from start of theRes to resStr }
  147.                resStr := StringPtr(ORD4(resStr) + 1 + ORD4(resStr^[0]));
  148.            end;
  149.  
  150.  
  151.           { move old data forward }
  152.           oldSize := GetHandleSize(theRes);
  153.         
  154.         newSize := oldSize - ORD4(resStr^[0]) - 1;
  155.  
  156.         srcPtr := Ptr(ORD4(resStr) + LENGTH(resStr^) + 1);    { the start of the string following string to delete }
  157.         dstPtr := Ptr(resStr);                                { the start of the string to delete }
  158.         byteCount := oldSize - offset - ORD4(resStr^[0]) - 1;{ theRes's size less offset to delete string less delete string's size }
  159.         
  160.            BlockMoveData(srcPtr, dstPtr, byteCount);
  161.            
  162.  
  163.            { shrink resource handle to eliminate unused space }
  164.            if newSize < oldSize then
  165.            begin
  166.                HUnlock(theRes);
  167.                SetHandleSize(theRes,newSize);
  168.                if (MemError <> noErr) then
  169.                begin
  170.                    theErr := MemError;
  171.                    goto 100; 
  172.                end;
  173.                HLock(theRes);
  174.            end;
  175.         
  176.         numStrings := numStrings - 1;
  177.            BlockMoveData(@numStrings, theRes^, sizeof(SInt16));
  178.  
  179.         { write resource out }
  180.  
  181.            ChangedResource(theRes);
  182.            WriteResource(theRes);
  183.            HPurge(theRes);
  184.  
  185.            theErr := ResError;
  186. 100:
  187.         if theRes <> nil then
  188.             ReleaseResource(theRes);
  189.            DeleteIndString := theErr;
  190.     end;
  191.  
  192.  
  193.  
  194. {*********************************SetIndString**************************************}
  195. {    Replaces an individual string in a string list (STR#) resource. The parameters         }
  196. {    are: theStr is the new string, resID is the STR# resource ID, strIndex is the         }
  197. {    number of the individual string to be replaced.                                        }
  198.     function SetIndString(theStr: Str255; resID, strIndex: integer): OSErr;
  199.         var
  200.            theRes: Handle;          { handle pointing to STR# resource             }
  201.            numStrings,                 { number of strings in STR#                 }
  202.            ourString: integer;      { counter to index up to strIndex             }
  203.            resStr: StringPtr;          { string pointer to STR# string to replace     }
  204.            oldSize,                    { size of STR# resource before call         }
  205.            newSize: longint;        { size of STR# resource after call             }
  206.            offset: UInt32;            { resource offset to str to replace         }
  207.            theErr: OSErr;            { error code                                }
  208.            oldStringSize, newStringSize: integer;{ sizes of old and new strings    }
  209.            srcPtr, dstPtr: Ptr;        { source and destination pointers and …        }
  210.            byteCount:integer;        { amount to copy for BlockMoveData call        }
  211.         const
  212.             kStringOutOfRange = -100;
  213.         label
  214.             100;
  215.     begin
  216.            { make sure string exists }
  217.         if strIndex < 1 then
  218.         begin
  219.             theErr := kStringOutOfRange;
  220.             goto 100;
  221.         end;
  222.            { make sure resource exists }
  223.         theRes := GetResource(kStringListType, resID);
  224.            if (theRes = nil) then
  225.            begin
  226.                theErr := ResError;
  227.                goto 100; 
  228.            end;
  229.  
  230.            HLockHi(theRes);
  231.            HNoPurge(theRes);
  232.  
  233.            { get # of strings in STR# }
  234.  
  235.            BlockMoveData(theRes^, @numStrings, sizeof(SInt16));
  236.            if (strIndex > numStrings) then
  237.            begin
  238.                theErr := kStringOutOfRange;
  239.                goto 100;
  240.            end;
  241.  
  242.            { get a pointer to the string to replace }
  243.         
  244.            offset := sizeof(SInt16);
  245.            resStr := StringPtr(ORD4(theRes^) + sizeof(SInt16)); { pointer to the first string }
  246.            for ourString := 1 to strIndex - 1 do
  247.            begin
  248.                offset := offset + 1 + ORD4(resStr^[0]);
  249.                resStr := StringPtr(ORD4(resStr) + 1 + ORD4(resStr^[0]));
  250.            end;
  251.  
  252.            { grow resource handle to make room for new string }
  253.            oldSize := GetHandleSize(theRes);
  254.            oldStringSize := LENGTH(resStr^) + 1;
  255.            newStringSize := LENGTH(theStr) + 1;
  256.            newSize := oldSize - oldStringSize + newStringSize;
  257.            if newSize > oldSize then
  258.            begin
  259.                HUnlock(theRes);
  260.                SetHandleSize(theRes, newSize);
  261.                if (MemError <> noErr) then
  262.                begin
  263.                    theErr := MemError;
  264.                    goto 100; 
  265.                end;
  266.                HLock(theRes);
  267.            end;
  268.         
  269.         resStr := StringPtr(ORD4(theRes^) + offset);{ forgot to reset resStr after resizing }
  270.                                                     { theRes, (memory may have been moved!)    }
  271.         if newSize <> oldSize then
  272.         begin { move old data forward/backward to make room }
  273.             srcPtr := Ptr(ORD4(resStr) + LENGTH(resStr^) + 1);    { the start of the string following target string }
  274.             dstPtr := Ptr(ORD4(resStr) + LENGTH(theStr) + 1);    { the start of the string following replacement string }
  275.             byteCount := oldSize - offset - ORD4(resStr^[0]) - 1; 
  276.             
  277.                BlockMoveData(srcPtr, dstPtr, byteCount);
  278.            end;
  279.            
  280.            { move new data in }
  281.  
  282.            BlockMoveData(@theStr, resStr, LENGTH(theStr) + 1);
  283.  
  284.            { shrink resource handle to eliminate unused space }
  285.            if newSize < oldSize then
  286.            begin
  287.                HUnlock(theRes);
  288.                SetHandleSize(theRes,newSize);
  289.                if (MemError <> noErr) then
  290.                begin
  291.                    theErr := MemError;
  292.                    goto 100; 
  293.                end;
  294.                HLock(theRes);
  295.            end;
  296.  
  297.            { write resource out }
  298.  
  299.            ChangedResource(theRes);
  300.            WriteResource(theRes);
  301.  
  302.            theErr := ResError;
  303. 100:
  304.         if theRes <> nil then
  305.             ReleaseResource(theRes);
  306.            SetIndString := theErr;
  307.     end;
  308.  
  309.  
  310.  
  311. {*********************************InsertIndString**************************************}
  312. {    Inserts a new string into a string list (STR#) resource. The parameters are: theStr }
  313. {    is the new string, resID is the STR# resource ID, strIndex is the number of    the     }
  314. {    string before which the new string is inserted.                                        }
  315.     function InsertIndString(theStr: Str255; resID, strIndex: integer): OSErr;
  316.         var
  317.            theRes: Handle;          { handle pointing to STR# resource             }
  318.            numStrings,                 { number of strings in STR#                 }
  319.            ourString: integer;      { counter to index up to strIndex             }
  320.            resStr: StringPtr;          { string pointer to STR# string to replace     }
  321.            oldSize,                    { size of STR# resource before call         }
  322.            newSize: longint;        { size of STR# resource after call             }
  323.            offset: UInt32;            { resource offset to str to replace         }
  324.            theErr: OSErr;            { error code                                }
  325.            srcPtr, dstPtr: Ptr;        { source and destination pointers and …        }
  326.            byteCount:integer;        { amount to copy for BlockMoveData call        }
  327.         const
  328.             kStringOutOfRange = -100;
  329.         label
  330.             100;
  331.     begin
  332.         if strIndex < 1 then     { strIndex is the number of the string before which }
  333.         begin                    { we will insert the new string.                     }
  334.             theErr := kStringOutOfRange;
  335.             goto 100;
  336.         end;
  337.            { make sure resource exists }
  338.         theRes := GetResource(kStringListType, resID);
  339.            if (theRes = nil) then
  340.            begin
  341.                theErr := ResError;
  342.                goto 100; 
  343.            end;
  344.  
  345.            HLockHi(theRes);
  346.            HNoPurge(theRes);
  347.  
  348.            { get # of strings in STR# }
  349.  
  350.            BlockMoveData(theRes^, @numStrings, sizeof(SInt16));
  351.            if (strIndex > numStrings) then
  352.             strIndex := numStrings + 1;
  353.         
  354.            oldSize := GetHandleSize(theRes);
  355.         newSize := oldSize + ORD4(theStr[0]) + 1;
  356.         
  357.            { grow resource handle to make room for new string }
  358.  
  359.            HUnlock(theRes);
  360.            SetHandleSize(theRes, newSize);
  361.            if (MemError <> noErr) then
  362.            begin
  363.                theErr := MemError;
  364.                goto 100; 
  365.            end;
  366.            HLock(theRes);
  367.  
  368.            { get a pointer to the string to insert before }
  369.         
  370.         if strIndex <= numStrings then
  371.         begin
  372.                offset := sizeof(SInt16);
  373.                resStr := StringPtr(ORD4(theRes^) + sizeof(SInt16)); { pointer to the first string }
  374.                for ourString := 1 to strIndex - 1 do
  375.                begin
  376.                    offset := offset + 1 + ORD4(resStr^[0]);
  377.                    resStr := StringPtr(ORD4(resStr) + 1 + ORD4(resStr^[0]));
  378.                end;
  379.               { move old data backward to make room }
  380.             
  381.             srcPtr := Ptr(resStr);                                { the string to insert before is moved }
  382.             dstPtr := Ptr(ORD4(resStr) + LENGTH(theStr) + 1);    { back to accommodate the new string }
  383.             byteCount := oldSize - offset; 
  384.             
  385.                BlockMoveData(srcPtr, dstPtr, byteCount);
  386.                
  387.                { move new data in }
  388.     
  389.                BlockMoveData(@theStr, resStr, LENGTH(theStr) + 1);
  390.            end
  391.            else                    { the string to insert before doesn't exist so    }
  392.            begin                    { we add the string to the end of the resource     }
  393.                BlockMoveData(@theStr, Ptr(ORD4(theRes^) + oldSize), LENGTH(theStr) + 1);
  394.            end;
  395.  
  396.         numStrings := numStrings + 1;
  397.            BlockMoveData(@numStrings, theRes^, sizeof(SInt16));
  398.  
  399.            { write resource out }
  400.  
  401.            ChangedResource(theRes);
  402.            WriteResource(theRes);
  403.  
  404.            theErr := ResError;
  405. 100:
  406.         if theRes <> nil then
  407.             ReleaseResource(theRes);
  408.            InsertIndString := theErr;
  409.     end;
  410.  
  411. end.