[comp.sys.mac] Adding to STR# resources

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