Newsgroups: comp.sys.mac.programmer
Subject: Re: Parameter Types for GetIndString, ParamText, NewString, et al.
From: Bruce.Hoult@bbs.actrix.gen.nz
Date: Sat, 25 Jul 1992 12:12:51 GMT
Organization: Actrix Information Exchange

In article <1992Jul25.002622.1@tesla.njit.edu> erh0362@tesla.njit.edu (Elliotte Rusty Harold) writes:
> 
> 	Can anyone spot the problem in the following code?

Well, uh, just one or two.. :-)


> It typically crashes 
> at either the GetIndString, the ParamText, or the StopAlert call.  I suspect 
> I'm not passing the proper type of argument to one of the toolbox calls (e.g. 
> sending a Str255 where I should be passing a StringPtr or vice versa) but I've 

No, you're corrupting the heap.


> tried this with many different combinations of variable 
> type without success.  The various 
> sample source I've looked at has been inconsistent on the type of argument 
> passed to these toolbox calls, i.e. some pass strings and some pass StringPtr's.

Once you start mucking around behind the compiler's back by using *
and & the declared types or variables matters not one bit -- it becomes
*your* responsibility to know what you're doing.

Read the documentation.  Understand in particular where the Toolbox is
going to return a value to you and make sure you're very sure about
where it is going to write that information for you and that there is
enough space for what it is going to write.


> Do I need to allocate space for errorString (a Str255) or is it automatically 
> allocated since errorString is really an array of 256 chars?  If I 
> do need to allocate space, how?

No you don't, because it is a local variable.  You need to learn what
a C compiler does with local variables -- where they are allocated
space (on the stack) and when (allocated at function entry,
deallocated at function exit).


> Can I dispense with the StringHandle 
> errorStringH  entirely and just use errorString and &errorString as necessary? 
> All suggestions appreciated.  Problem function follows.

Yes.  Just look at the definition of GetIndString:

---------
PROCEDURE GetIndString(VAR theString: Str255;strListID: INTEGER;index: INTEGER);
File {PInterfaces}ToolUtils.p
Inside Macintosh reference: I-468
Moves or purges memory.
[Not in ROM]
 
GetIndString returns in theString a string in the string list that has the resource
ID strListID. It reads the string list from the resource file if necessary, by calling
the Resource Manager function GetResource('STR#',strListID). It returns the string
specified by the index parameter, which can range from 1 to the number of strings in
the list. If the resource cant be read or the index is out of range, the empty
string is returned.
---------

This means that you have to somehow allocate enough space for a 255 character
string, and then tell GetIndString where it is.  It could be a local variable
(like your "errorString" or on the heap (like your errorStringH), as long
as it's big enough.

Let's have a look at your code as it stands:

> Boolean IOCheck(OSErr errornumber) {
> 
> 	Str255	 errorString;
> 	StringHandle errorStringH;	
> 
> 	if (errornumber != noErr) {
> 
> 		errorStringH = NewString(&errorString);

Do you know what NewString does?  Let's look in TFM, shall we?

----------
FUNCTION NewString(theString: Str255): StringHandle;
	INLINE $A906;
File {PInterfaces}ToolUtils.p
Inside Macintosh reference: I-468
Trap number: A906
Moves or purges memory.
NewString allocates the specified string as a relocatable object in the heap and
returns a handle to it.
 
Note:  NewString returns a handle to a string whose size is based on its
       actual length (not necessarily 255); if you're going to use Pascal
       string functions that could change the length of the string, you may
       want to call SetString or the Memory Manager procedure SetHandleSize
       first to set the string to the maximum size.
----------

Read that note carefully -- it's there to help stop you making a stupid
mistake.

NewString is taking what's in your string variable (errorString) and
copying it into a *new* string in a handle.  What's in errorString?
You don't know -- you've never put anything in it so it could be
totally random.  The chances are though, that it's pretty short -- the
chances are excellent that it's zero to ten characters long.  That means
that errorStringH points to something that it probably much smaller
than is needed to hold a full Str255.


> 		HLock(errorStringH);
> 		GetIndString(**errorStringH, IO_ERROR_STRINGS, 
> 			-1 * errornumber - ZEROTH_IO_ERROR );

BANG!  You probably just corrupted the heap by writing past the end of
the space pointed to by errorStringH.

Either errorString or &errorString would have been fine here (either because
C automatically takes the address of an array in a parameter context
-- bletch).  Either *errorStringH (which I'd prefer) or your
**errorStringH would have been fine if you'd made sure there was
enough space for the result -- something like "errorStringH =
NewHandle(256)" would have been fine instead of the NewString.


> 		if ( errorString == NIL_POINTER) {

Huh??

Three things wrong here:

1) errorString has nothing to do with errorStringH at this point.
errorStringH points at a *copy* of errorString, not at the original.
The GetIndString will change the copy but not the original.

2) GetIndString returns an empty string on error, not a null pointer.
RTFM above -- and there's no way it *could* return a null pointer,
since you're supplying the storage for it to alter, not the other way around.

You should be testing **errorStringH == 0 or (*errorStringH)[0] == 0 or
errorString[0] == 0 (if you weren't using the unnecesary errorStringH).


3) It's not possible for "errorString == NIL_POINTER" to ever be true,
since errorString (really &errorString because of C's automatic conversion) is
a local variable on the stack and thus can never have an address of zero.


The rest of the code looks OK.

You've got two options:

1) junk errorString, change the NewString to NewHandle and
"errorString == NIL_POINTER" to "**errorStringH == 0".

or

2) junk errorStringH and use errorString or &errorString (the & will
always be reundant in C) and "errorString[0] == 0".

-- Bruce
   (who didn't mean to be as abusive as he probably sounded)

-- 
Bruce.Hoult@bbs.actrix.gen.nz   Twisted pair: +64 4 477 2116
BIX: brucehoult                 Last Resort:  PO Box 4145 Wellington, NZ
"Cray's producing a 200 MIPS personal computer with 64MB RAM and a 1 GB
hard disk that fits in your pocket!"   "Great!  Is it PC compatable?"