Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

What the heck??? FSRefMakePath() weirdness

42 views
Skip to first unread message

Don Bruder

unread,
Apr 14, 2007, 1:16:22 PM4/14/07
to

I'm stumped -

Mac OS X 10.3.9, coding to Carbon, using the newest XCode (1.5) that
will run under 10.3.9.

I drop one or more files on my app. Or double-click it/them to get them
to be opened by the app. I need to display the path(s) for status
display and debugging use. So as part of the odoc event handler
processing, I retrieve the FSRef(s) of the dropped/double-clicked
file(s) from the odoc event and stuff them into an array of FSRefs
(defined as FSRef TheFSRefs[1024]; ) and then later, use it/them as
input to FSRefMakePath() to get the path(s) back so I can display
it/them.

I've got the following defined in the display routine:

UInt8 ThePathBuff[2048];
UInt32 PathLength;
OSStatus Result;

I make the call to FSRefMakePath() within a for() loop, like so:
for (I=0;I<FileCount;I++)
{
Result = FSRefMakePath(&TheFSRefs[I], &ThePathBuff[0], PathLength);
FormatAndDisplay(ThePathLength, &ThePathBuff[0]);
}

For one group of files (let's say it's located in
"/Volumes/Drive/Subdirectory/Another Subdirectory/" to save me typing
the full 160+ character pathnames) everything works exactly as expected
- On each pass through the loop, I get back a Result value of noErr, the
correct value in PathLength, and ThePathBuff holding the string
"/Volumes/Drive/Subdirectory/Another Subdirectory/one of the filenames".

For another group of files, located at "/Volumes/Drive/Subdirectory/"
(Note that everything is the same, just one subdirectory closer to the
root) I hit a snag -

Instead of getting back good results like above, for each pass through
the loop, I get a Result value of -2110 (Which I've looked up and found
to be "PathTooLong") a PathLength value of 2, and ThePathBuff holding
absolutely nothing.

PathTooLong?!?!? The path to the second group of files is shorter than
that of the first group - INCLUDING the filenames - the files are on the
same drive, and everything is being done exactly the same as the first
group, which WORKS. What's up with that?!?!?

If I move up to "Volumes/Drive/" and open/drop some files, I get the
same thing - -2110 result, Pathlength = 2, and ThePathBuff empty.

Even worse, I'm finding exactly diddly-squat in the docs to explain this
(mis)behavior.

Any thoughts? Why does a group of files with pathnames in the 160-ish
character range work, while a group that has shorter pathnames (40-100
chars) fails, with FSRefMakePath() complaining that the pathnames are
too long on the second group? This make no sense at all...

--
Don Bruder - dak...@sonic.net - If your "From:" address isn't on my whitelist,
or the subject of the message doesn't contain the exact text "PopperAndShadow"
somewhere, any message sent to this address will go in the garbage without my
ever knowing it arrived. Sorry... <http://www.sonic.net/~dakidd> for more info

Gregory Weston

unread,
Apr 14, 2007, 4:05:12 PM4/14/07
to
In article <46210c40$0$27185$742e...@news.sonic.net>,
Don Bruder <dak...@sonic.net> wrote:

> I've got the following defined in the display routine:
>
> UInt8 ThePathBuff[2048];
> UInt32 PathLength;
> OSStatus Result;
>
> I make the call to FSRefMakePath() within a for() loop, like so:
> for (I=0;I<FileCount;I++)
> {
> Result = FSRefMakePath(&TheFSRefs[I], &ThePathBuff[0], PathLength);
> FormatAndDisplay(ThePathLength, &ThePathBuff[0]);
> }
>
> For one group of files (let's say it's located in
> "/Volumes/Drive/Subdirectory/Another Subdirectory/" to save me typing
> the full 160+ character pathnames) everything works exactly as expected
> - On each pass through the loop, I get back a Result value of noErr, the
> correct value in PathLength, and ThePathBuff holding the string
> "/Volumes/Drive/Subdirectory/Another Subdirectory/one of the filenames".
>
> For another group of files, located at "/Volumes/Drive/Subdirectory/"
> (Note that everything is the same, just one subdirectory closer to the
> root) I hit a snag -
>
> Instead of getting back good results like above, for each pass through
> the loop, I get a Result value of -2110 (Which I've looked up and found
> to be "PathTooLong") a PathLength value of 2, and ThePathBuff holding
> absolutely nothing.

Um. Are you saying you "get" a PathLength of 2? As in, that's what the
value of that variable is after FSRefMakePath returns?

If so, that's probably the issue right there. The 3rd argument to that
function is _input_. You tell it how large the buffer is. If, on input,
it doesn't indicate that the buffer is large enough for the name,
PathTooLong seems like quite a reasonable response.

You're probably just getting lucky on some invocations of your function
that the value of PathLength is getting random garbage on the stack that
happens to be a large enough value to hold the paths. (Of course, in
theory, if it's bigger than your buffer really is there's a potential
for amusing crashes.)

I'm going to put a plug in here for a habit that you should really get
into: Always initialize your variables when you declare them.
Determinism is a wonderful thing in code, and accepting what's on the
stack when you allocate local variables is about as close as you'll get
to a truly non-deterministic number on a computer.

G

Don Bruder

unread,
Apr 14, 2007, 6:15:37 PM4/14/07
to
In article <uce-603F0E.1...@comcast.dca.giganews.com>,
Gregory Weston <u...@splook.com> wrote:

Precisely - For the cases where it works correctly, the PathLength value
comes back correctly as the length of the path that corresponds to the
FSRef I sent in.

>
> If so, that's probably the issue right there. The 3rd argument to that
> function is _input_. You tell it how large the buffer is. If, on input,
> it doesn't indicate that the buffer is large enough for the name,
> PathTooLong seems like quite a reasonable response.

???

And ??? again!

Looking at the FSRefMakePath() docs, it reads as though PathLength is an
output, not an input? (Although looking at it again, this is *VERY*
unclear... The phrasing used could mean either...)

> You're probably just getting lucky on some invocations of your function
> that the value of PathLength is getting random garbage on the stack that
> happens to be a large enough value to hold the paths. (Of course, in
> theory, if it's bigger than your buffer really is there's a potential
> for amusing crashes.)

I'm sure it could if it's supposed to be an INPUT! That would also
explain a couple of crashes early in the development of the display
routine...

The puzzling part is that it currently works *EVERY* time for the first
scenario ("/Vol/drv/sub1/sub2/files"), including having it come back as
the correct length for the newly made path (strlen(ThePathBuff) ==
PathLength) and pukes *EVERY* time for anything else, with PathLength ==
2.

How bizarre...

If it's an input, though, I guess I need to init PathLength before the
call - I've been going with the concept that it's an OUTPUT, and
expecting to see the length of the path returned in it. Let's see what
it does when I init the variable before the call...

<time passes as an edit/build/test cycle happens>

Sure enough... Stuffing 2048 into PathLength before each call gets me
the expected result for all test cases I ran through it, with PathLength
holding the value 2048 after the call each and every time.

> I'm going to put a plug in here for a habit that you should really get
> into: Always initialize your variables when you declare them.
> Determinism is a wonderful thing in code, and accepting what's on the
> stack when you allocate local variables is about as close as you'll get
> to a truly non-deterministic number on a computer.

Heh, yeah, I can't argue with that.

However, when one is using a local as a parameter and expecting it to be
output from the function one is calling, one expects that calling the
function is going to set the variable to a sane value!

This isn't the first time I've been bitten by ambiguity in Apple's
docs... It seems that they frequently read as if a parameter is an
input, or vice-versa, without explicitly stating which it actually is,
and my code repeatedly blows up in my face for no apparent reason -
until I twig to the idea that one of the parameters is actually input,
rather than output, or vice-versa.

Ah well, one of these days I'll learn to tell which is actually meant
when the docs aren't explicit.

Thanks, Greg. I would have kept on looking "through" this problem for a
*LONG* time (if I didn't just give up in frustration) without your
input.

Now, back to my irregularly scheduled coding, perhaps already in
progress - or perhaps not! :)


>
> G

Reinder Verlinde

unread,
Apr 14, 2007, 6:32:28 PM4/14/07
to
In article <46215263$0$27197$742e...@news.sonic.net>,
Don Bruder <dak...@sonic.net> wrote:

> Looking at the FSRefMakePath() docs, it reads as though PathLength is an
> output, not an input? (Although looking at it again, this is *VERY*
> unclear... The phrasing used could mean either...)
>

> [...]


>
> This isn't the first time I've been bitten by ambiguity in Apple's
> docs... It seems that they frequently read as if a parameter is an
> input, or vice-versa, without explicitly stating which it actually is,
> and my code repeatedly blows up in my face for no apparent reason -
> until I twig to the idea that one of the parameters is actually input,
> rather than output, or vice-versa.
>
> Ah well, one of these days I'll learn to tell which is actually meant
> when the docs aren't explicit.

In this case, the prototype of the function should have given that away.
There is no way in C to have a non-pointer argument being an output
parameter.

I also have the impression that the descriptions of output parameters
always contain either of the phrases "On return," or "On output,"

Reinder

Gregory Weston

unread,
Apr 15, 2007, 8:36:15 AM4/15/07
to
In article <46215263$0$27197$742e...@news.sonic.net>,
Don Bruder <dak...@sonic.net> wrote:

> The puzzling part is that it currently works *EVERY* time for the first
> scenario ("/Vol/drv/sub1/sub2/files"), including having it come back as
> the correct length for the newly made path (strlen(ThePathBuff) ==
> PathLength) and pukes *EVERY* time for anything else, with PathLength ==
> 2.

That has to be luck, for the simple reason that you're not passing the
function a reference to an integer. You're passing a value, which means
the FSRefMakePath won't (can't) update your local variable.

>
> How bizarre...
>
> If it's an input, though, I guess I need to init PathLength before the
> call - I've been going with the concept that it's an OUTPUT, and
> expecting to see the length of the path returned in it. Let's see what
> it does when I init the variable before the call...
>
> <time passes as an edit/build/test cycle happens>
>
> Sure enough... Stuffing 2048 into PathLength before each call gets me
> the expected result for all test cases I ran through it, with PathLength
> holding the value 2048 after the call each and every time.
>
> > I'm going to put a plug in here for a habit that you should really get
> > into: Always initialize your variables when you declare them.
> > Determinism is a wonderful thing in code, and accepting what's on the
> > stack when you allocate local variables is about as close as you'll get
> > to a truly non-deterministic number on a computer.
>
> Heh, yeah, I can't argue with that.
>
> However, when one is using a local as a parameter and expecting it to be
> output from the function one is calling, one expects that calling the
> function is going to set the variable to a sane value!

Aside from the pass-by-reference issue above, though, I still consider
it useful to initialize even variables I know are only output. Just a
couple of days ago someone asked me a question about some code that
looked like this:

id myobj;
int myerr;
myobj = [some_object some_method:37 error:&myerr];
if(myerr)
{
...
}

This code is potential death if some_method:error: doesn't go to the
effort to clear what its second argument points to in the case of
success. (And it didn't.)


G

Michael Ash

unread,
Apr 15, 2007, 8:42:16 AM4/15/07
to
Gregory Weston <u...@splook.com> wrote:
> Aside from the pass-by-reference issue above, though, I still consider
> it useful to initialize even variables I know are only output. Just a
> couple of days ago someone asked me a question about some code that
> looked like this:
>
> id myobj;
> int myerr;
> myobj = [some_object some_method:37 error:&myerr];
> if(myerr)
> {
> ...
> }
>
> This code is potential death if some_method:error: doesn't go to the
> effort to clear what its second argument points to in the case of
> success. (And it didn't.)

I would say that this code is dangerous regardless. In general, there are
no guarantees made about the state of the error parameter upon success.
Obviously this will depend on the conventions of the code you're calling,
but for example with Cocoa methods which return an NSError ** by
reference, I don't think they make any guarantee about that parameter if
the method succeeds. It would make sense for them to just leave it alone,
but I don't think it would be contrary to the documentation for them to
fill it with 0xdeadbeef before returning success.

In conclusion: beware. :)

--
Michael Ash
Rogue Amoeba Software

Gregory Weston

unread,
Apr 15, 2007, 1:03:07 PM4/15/07
to
In article <11766409...@nfs-db1.segnet.com>,
Michael Ash <mi...@mikeash.com> wrote:

> Gregory Weston <u...@splook.com> wrote:
> > Aside from the pass-by-reference issue above, though, I still consider
> > it useful to initialize even variables I know are only output. Just a
> > couple of days ago someone asked me a question about some code that
> > looked like this:
> >
> > id myobj;
> > int myerr;
> > myobj = [some_object some_method:37 error:&myerr];
> > if(myerr)
> > {
> > ...
> > }
> >
> > This code is potential death if some_method:error: doesn't go to the
> > effort to clear what its second argument points to in the case of
> > success. (And it didn't.)
>
> I would say that this code is dangerous regardless. In general, there are
> no guarantees made about the state of the error parameter upon success.

Absolutely. Any time you rely on behavior that isn't documented you're
on your own. The method my other correspondent was asking about was
documented to signal an error condition by returning nil. I recommended
that he ignore his error output completely until verifying by this
documented means that an error had actually occurred.

G

Jan E. Schotsman

unread,
Apr 19, 2007, 11:40:33 AM4/19/07
to
On 2007-04-14 22:05:12 +0200, Gregory Weston <u...@splook.com> said:

> I'm going to put a plug in here for a habit that you should really get
> into: Always initialize your variables when you declare them.
> Determinism is a wonderful thing in code, and accepting what's on the
> stack when you allocate local variables is about as close as you'll get
> to a truly non-deterministic number on a computer.

OTOH some variables have no natural initial value.Initializing them to
0 or -1 or whatever makes code less natural.
Leaving them uninitialized can uncover bugs. Randomize them all during
testing might be even better.
For example, I was recently drawing Bezier curves and it should'nt
matter in which direction the curve is traversed. But the random value
for direction did make a bug in my code happen sooner.

Jan E.

David Phillip Oster

unread,
Apr 19, 2007, 11:55:01 AM4/19/07
to
In article <46278d71$0$28908$e4fe...@dreader17.news.xs4all.nl>,

Jan E. Schotsman <jesch...@SPAMMExs4all.nl> wrote:

> On 2007-04-14 22:05:12 +0200, Gregory Weston <u...@splook.com> said:
>
> > I'm going to put a plug in here for a habit that you should really get
> > into: Always initialize your variables when you declare them.
> > Determinism is a wonderful thing in code, and accepting what's on the
> > stack when you allocate local variables is about as close as you'll get
> > to a truly non-deterministic number on a computer.
>
> OTOH some variables have no natural initial value.Initializing them to
> 0 or -1 or whatever makes code less natural.

I think "natural" does not mean what you think it means.

> Leaving them uninitialized can uncover bugs. Randomize them all during
> testing might be even better.
> For example, I was recently drawing Bezier curves and it should'nt
> matter in which direction the curve is traversed. But the random value
> for direction did make a bug in my code happen sooner.
>
> Jan E.


No, leaving them uninitialized means the bug doesn't show up until the
most expensive time.

Yes, you should have unit tests. You can even have a global
gUninitializedValue, assign to your locals from it, and depending on the
test set it to 0xDEADBEEF and swap it to a different value on successive
tests.

See "Test Driving Your Code with OCUnit"
http://developer.apple.com/tools/unittest.html

BY the way, a common mistake is failing to initialize variables, then
passing to them to some routine as an "out" parameter. For example:

NSString *errorString;
NSData *data = [NSPropertyListSerialization
[NSPropertyListSerialization dataFromPropertyList:plist format:format
errorDescription:&errorString];

[errorString release]; // correct or not?

Since this example failed to initialize errorString, if
dataFromPropertyList did not assign to it, you'd be releasing junk.
but if dataFromPropertyList did assign to it, you are required to
release it.

Writing NSString *errorString = nil;
avoids the error.

Conclusion:

Write correct code. No one cares if it is "natural." No one even cares
if it is fast. If you let yourself write broken code, you can make it
arbitrarily fast. Write correct code.

Reinder Verlinde

unread,
Apr 19, 2007, 11:53:57 AM4/19/07
to
In article <46278d71$0$28908$e4fe...@dreader17.news.xs4all.nl>,
Jan E. Schotsman <jesch...@SPAMMExs4all.nl> wrote:

> On 2007-04-14 22:05:12 +0200, Gregory Weston <u...@splook.com> said:
>
> > I'm going to put a plug in here for a habit that you should really get
> > into: Always initialize your variables when you declare them.
> > Determinism is a wonderful thing in code, and accepting what's on the
> > stack when you allocate local variables is about as close as you'll get
> > to a truly non-deterministic number on a computer.
>
> OTOH some variables have no natural initial value.Initializing them to
> 0 or -1 or whatever makes code less natural.

In such cases, I tend to write something like:

int const kAnyValueWillDo = 135674;
someFunction(..., kAnyValueWillDo, ...);


> Leaving them uninitialized can uncover bugs. Randomize them all during
> testing might be even better.

Yes, that is a decent idea in test code for the library in which the
function is defined. In the application using that, initializing them
with a constant is better because it diminishes the number of test path.
So, in production code, I would not do it.

> For example, I was recently drawing Bezier curves and it should'nt
> matter in which direction the curve is traversed. But the random value
> for direction did make a bug in my code happen sooner.

Hm, I really should name such constants kAnyValueShouldDo :-)

Also: that might be true, but did that offset the fact that the
intermittent nature of the bug made it harder to find and fix it?

Reinder

Michael Ash

unread,
Apr 19, 2007, 4:34:08 PM4/19/07
to
David Phillip Oster <os...@ieee.org> wrote:
> BY the way, a common mistake is failing to initialize variables, then
> passing to them to some routine as an "out" parameter. For example:
>
> NSString *errorString;
> NSData *data = [NSPropertyListSerialization
> [NSPropertyListSerialization dataFromPropertyList:plist format:format
> errorDescription:&errorString];
>
> [errorString release]; // correct or not?
>
> Since this example failed to initialize errorString, if
> dataFromPropertyList did not assign to it, you'd be releasing junk.
> but if dataFromPropertyList did assign to it, you are required to
> release it.
>
> Writing NSString *errorString = nil;
> avoids the error.

Actually, this is not the case. The method is explicitly documented to set
errorString to nil on success and an error string on failure.

Other methods of this type typically make *no* guarantees about the error
variable on success, to the point where you cannot count on its value
being untouched.

The only safe thing to do in that situation is to actually check for
success and don't touch the error variable unless a failure actually
occurred. Initializing the variable in that case will actually just hide
your bug, since it's likely that under the covers they really do leave it
alone, even if you're not allowed to rely on it.

Gregory Weston

unread,
Apr 20, 2007, 12:43:36 AM4/20/07
to
In article <46278d71$0$28908$e4fe...@dreader17.news.xs4all.nl>,
Jan E. Schotsman <jesch...@SPAMMExs4all.nl> wrote:

> On 2007-04-14 22:05:12 +0200, Gregory Weston <u...@splook.com> said:
>
> > I'm going to put a plug in here for a habit that you should really get
> > into: Always initialize your variables when you declare them.
> > Determinism is a wonderful thing in code, and accepting what's on the
> > stack when you allocate local variables is about as close as you'll get
> > to a truly non-deterministic number on a computer.
>
> OTOH some variables have no natural initial value.Initializing them to
> 0 or -1 or whatever makes code less natural.

I disagree. Strongly. You've got pretty much two choices available for
initialization:
1. You can initialize to some reasonable default value.
2. You can initialize to a known value that's of the appropriate type
but contextually invalid.

Either way, you've eliminated the potential for very annoying problems
that crop up as a result of your code being non-deterministic, and
there's nothing at all unnatural about that.

> Leaving them uninitialized can uncover bugs.

Or even introduce them. You're supposed to be uncovering bugs during
testing after all. Finding them after deployment because you've
unnecessarily hit a degenerate case that never happened to come up in
testing is a Bad Thing.

G

0 new messages