jmm@thoth6.berkeley.edu.BERKELEY.EDU (01/14/88)
How does one add strings onto the end of a STR# resource from inside a program? James Moore ------------------------- | / Go raibh sonas agus / | jmm@bartleby.berkeley.edu / rath oraibh an / |--------------------------------------------| / Nollaig agus san / | The University of California only knows | / Athbhliain! / | me as a number. They couldn't care less | / / | what my opinions are. | ------------------------- |--------------------------------------------|
earleh@eleazar.Dartmouth.EDU (Earle R. Horton) (01/15/88)
In article <6554@jade.BERKELEY.EDU>, > jmm@thoth6.berkeley.edu.BERKELEY.EDU writes: > How does one add strings onto the end of a STR# resource from > inside a program? ----------------------cut here---------------------- /* (Sorry if you wanted Pascal.) There is no ToolBox call to do this (correct me if I am wrong) so you have to do the following: a) Resize the handle to the resource. b) Manipulate the STR# resource data structure to make desired changes. c) Write out the modified resource. My printer driver article (MacTutor, December, 1987) gives an example of changing the contents of a string list, which is not very far different from this problem. If you have it, you can just modify that code for the task (it's on page 62). A string list is composed of a WORD which contains the number of strings in the list, followed by that number of Pascal strings, lined up one after the other. (A WORD is a two-byte integer.) The length of the string list in memory is found from the formula: String_list_length = 2 + value_of_header_word + sum_of_length_bytes + PAD 2 accounts for the header word, the value of the header word accounts for the length bytes, and the sum of the length bytes accounts for the actual string characters. A conservative value for "PAD" is 24. The first byte of a Pascal string gives the length in characters of the rest of the string. The following code will illustrate how we: a) Read in the string list from disk. b) Determine the desired new handle size. c) Modify the string list to contain another string. d) Write out the modified string list. The example is in LightSpeedC, where an 'int' is worth 2 bytes. If you want to test the example program, put it in a project, add a string list, ID #100, with ResEdit or something, and run it a few times to see what you get. You don't need any libraries. */ typedef struct{ unsigned int len; unsigned char text[1]; }stringlist,*pStrLst,**hStrLst; #define nil 0L #define FALSE 0 #define TRUE 1 #define PAD 24 /* * Tacks another Pascal string onto the end of a string list. * Returns FALSE on error, TRUE on success. Could check for more * errors! */ int tackstring(idnum,newstr) int idnum; unsigned char *newstr; { hStrLst TheList; unsigned char *strptr; int numstrings,i; long length,result; TheList = (hStrLst)GetResource('STR#',idnum); if (TheList == nil) return FALSE; /* Error handler time! */ LoadResource(TheList); asm{ move.l TheList,a0 ; I'm doing this so I don't HLock ; need the MacTraps library. } numstrings = (**TheList).len; /* How many? */ strptr = &(**TheList).text[0]; /* Point to first one. */ length = 2 + PAD + numstrings; /* Compute the length. */ for(i = numstrings; i-- ; ){ length += *(strptr += (*strptr) +1); } /* Now we point at one past the end. */ length += *newstr; /* Allow for expansion. */ /* Resize the handle, error could occur here! */ asm{ move.l TheList,a0 ;MM calls use regs. move.l length,d0 ;Resize the handle. SetHandleSize move.l TheList,a0 ;How much did we get? GetHandleSize move.l d0,result ;Tell C. } if( result != length) return FALSE; /* Error, give up. */ pstrcpy(strptr,newstr); /* Tack on the new one. */ (**TheList).len++; /* Don't forget to say one more. */ ChangedResource(TheList); /* Tell the Mac. */ WriteResource(TheList); /* Write out the resource. */ return TRUE; } pstrcpy(dst,src) /* Copy a pascal string, simple. */ unsigned char *src,*dst; { asm{ clr.l d0 move.l src,a0 move.l dst,a1 move.b (a0),d0 loop: move.b (a0)+,(a1)+ dbra d0,@loop } } /* * Very boring main() program. I assume you have a more interesting one * than this! */ main(){ tackstring(100,"\pNew Test String"); } -- ********************************************************************* *Earle R. Horton, H.B. 8000, Dartmouth College, Hanover, NH 03755 * *********************************************************************
lsr@apple.UUCP (Larry Rosenstein) (01/20/88)
A couple of comments about this: In article <7927@eleazar.Dartmouth.EDU> earleh@eleazar.Dartmouth.EDU (Earle R. Horton) writes: > >String_list_length = 2 + value_of_header_word + sum_of_length_bytes + PAD What is the purpose of the PAD? The MPW Rez tool doesn't generate any padding after the strings. There is no harm in adding padding, however. Second, a good call to use for this case (often overlooked) is Munger. Munger operates on the bytes in a handle and can insert, delete, or replace any sequence of bytes. By using Munger you don't have to worry about changing the handle size or moving bytes around (if you wanted to insert in the middle). Finally, one thing to watch out for is the fact that the individual strings can start on an odd byte boundary. I ran into this with my ApplicationMenu INIT, since menus items are stored the same way. You can't use a StringPtr variable to point to one of the strings, since such a pointer needs to be even. (This is at least true in MPW Pascal.) -- Larry Rosenstein Object Specialist Apple Computer AppleLink: Rosenstein1 UUCP: {sun, voder, nsc, mtxinu, dual}!apple!lsr CSNET: lsr@Apple.com