[comp.sys.mac] obtaining the full pathname of a file

steig@batcomputer.UUCP (06/11/87)

I am writing a program in Lightspeed C that asks the user for a filename
using the standard file package and then processes the text in that file
using some of the stdio C routines.  I use fopen() to open the file.

fopen() requires the pathname of the file, but SFGetFile and SFPutFile only
return the filename and the working directory reference number.  How do I
obtain the pathname of the file?
-- 
|Mark J. Steiglitz          |Bitnet: steig@crnlthry, araj@crnlvax5            |
|USnail: 66 Steiglitz Road  |Arpanet: steig@tcgould.tn.cornell.edu            |
|        Liberty, NY  12754 |         araj@vax5.ccs.cornell.edu               |
|                           |Usenet: steig@batcomputer.tn.cornell.edu.uucp    |

clive@drutx.ATT.COM (Clive Steward) (06/11/87)

in article <1345@batcomputer.tn.cornell.edu>, steig@batcomputer.tn.cornell.edu (Mark J. Steiglitz) says:
> 
> 
> I am writing a program in Lightspeed C that asks the user for a filename
> using the standard file package and then processes the text in that file
> using some of the stdio C routines.  I use fopen() to open the file.
> 
> fopen() requires the pathname of the file, but SFGetFile and SFPutFile only
> return the filename and the working directory reference number.  How do I
> obtain the pathname of the file?

Welcome to the party, Mark.  I just doped this out by fighting with
the HFS file system stuff in inside Mac IV for a whole night, and then
snooping someone elses code after all the heavy stuff got me nothing(!).

Getting the pathname is possible (LSC editor does it, if you ask), but
clearly involves traipsing the PBGetCatInfo trail.  Maybe someone who has
done this successfully would post some code; it would be enlightening for 
planning how to effectively do other things.

But apparently the real answer to your question is simple.  It means
taking Inside Mac's advice about not using full paths, but rather
depending on the working directory system.  And also believing what 
is said about playing fast and loose with vRefNums, which can actually
be either Volume or Working Directory references.

All it seems you have to do is take the vRefNum that you get back from 
SFGetFile, and feed that to SetVol before opening the name, e.g.:

    SFGetFile (SFPwhere, "\p", NULL, 1, typelist, NULL, &reply);

    if (reply.good) {

	SetVol ("\p", reply.vRefNum);    /* set Working Dir, actually */

	if ((fp = fopen (PtoCstr (reply.fname), "r - or whatever")) != NULL ) {
	    ...
	}
	...
    }

It's not Unix, but thinking of the SetVol as a kind of chdir will help.

The apparentlys and seems in the above are because the method works flawlessly
for me, except for one case.  

In a program I'm working on, I do an SFPutFile right after the above fragment.
This works fine, except, _only_ when there needs to be a disk swap to pick up 
the SFPutFile package.  Then SFPutFile becomes confused, and doesn't show 
the (proper or any) files in it's dialog.  I read in the last week a 
note that there's a bug in SFPutFile which sounds like it's probably this,
so probably there's not really any fault in the above.  (I was using system 3.2
at the time, think this is fixed in 4.1, but haven't tried to see yet).

Would someone like to elaborate about the bug, and how to get around
it (since users may not have the updated system)?


Clive Steward

dwb@apple.UUCP (06/12/87)

In article <1345@batcomputer.tn.cornell.edu> steig@tcgould.tn.cornell.edu.UUCP (Mark J. Steiglitz) writes:
>fopen() requires the pathname of the file, but SFGetFile and SFPutFile only
>return the filename and the working directory reference number.  How do I
>obtain the pathname of the file?
	...
	SFGetFile(where, "", NULL, 1, &types, NULL, &reply);
	if(!reply.good)
		return

	GetVol(NULL, &oldVol);		/* save def. vol. so we can restore */
	SetVol(NULL, reply.vRefNum);	/* change default vol. to selected */
	fp = fopen(ptocstr(reply.fName), "r");
	SetVol(NULL, oldVol);		/* restore original def. vol. */

	if(!fp)
	{
		...
	}
	...

-- 
	David W. Berry
	dwb@well.uucp                   dwb@Delphi
	dwb@apple.com                   293-0752@408.MaBell

cute@sphinx.uchicago.edu (John Cavallino) (06/12/87)

It seems that many people are interested in how to do this.  I have posted
some tested code (in pascal) to comp.sources.mac.  It includes a function which
takes a volume reference number, a file name and a directory ID and returns the
full path.  It checks for HFS vs. MFS and does the right thing in either case.
It even attempts to salvage things if the file turns out to be too deeply nested
for the full path to fit in a pascal string. Use and enjoy.
	Yours.
	   John C.



-- 
...ihnp4!gargoyle!sphinx!cute
(insert pithy quote here)

lsr@apple.UUCP (Larry Rosenstein) (06/13/87)

In article <204@drilex.UUCP> dricej@drilex.UUCP (Craig Jackson) writes:
>this.  Does anyone have any working code that they code post? (C, Pascal?)
>Does MacApp provide this facility?

MacApp doesn't provide this.  I wrote some code, which I have included
below.  (This was taken from an early version of the StdFile MPW tool, which
comes with MPW 2.0.)

IEString is a string type that is defined in the MPW integrated environment
unit.  This code doesn't check to see if your final pathname exceeds 255
characters.  This code did work, but I don't guarantee that I didn't make
any mistakes while pulling it out of the program.


Larry Rosenstein

UUCP:  {sun, voder, nsc, mtxinu, dual}!apple!lsr
CSNET: lsr@Apple.com

*******************************

VAR
	gotHFS:			BOOLEAN;
{ Setting the above variable is left as an exercise for the reader. }


{ Given a working directory refnum, return the volume refnum and dirID.
	This assumes that HFS is installed on the machine.  }

FUNCTION  GetDirID(VAR vRefnum: INTEGER; VAR dirID: LONGINT): OSErr;
VAR	pb:	WDPBRec;
	
BEGIN
WITH pb DO
	BEGIN
	ioCompletion := NIL;
	ioNamePtr := NIL;
	ioVRefnum := vRefnum;
	ioWDIndex := 0;
	ioWDProcID := 0;
	ioWDVRefnum := vRefnum;
	END;
GetDirID := PBGetWDInfo(@pb, FALSE);
vRefnum := pb.ioWDVRefnum;
dirID := pb.ioWDDirID;
END;


{ Given a volume refnum and dirID, return name of the volume / folder.  This
	routine assumes that HFS is installed on the machine. }
	
FUNCTION  DirID2Name(vRefnum: INTEGER; dirID: LONGINT; 
						VAR name: IEString): OSErr;
VAR	pb:	CInfoPBRec;
	temp:	Str255;
	err:	OSErr;
BEGIN
name := '';
err := noErr;

WITH pb DO
	BEGIN
	ioCompletion := NIL;
	ioNamePtr := @temp;
	ioVRefnum := vRefnum;
	ioDrDirID := dirID;
	END;
	
REPEAT
	pb.ioFDirIndex := -1;		{ get info about directory ioDirID }
	err := PBGetCatInfo(@pb, FALSE);
	IF err = noErr THEN
		BEGIN
		name := Concat(temp, ':', name);
		
		IF pb.ioDrDirID = 2 THEN { root directory }
			pb.ioDrDirID := 0
		ELSE
			pb.ioDrDirID := pb.ioDrParID;
		END;
UNTIL (err <> noErr) | (pb.ioDrDirID = 0);
DirID2Name := err;
END;


{ Given a volume or WD refnum, return name of the volume / folder. 
	This can be called whether or not HFS exists. 
This is the primary procedure that you would call from your program. }
	
FUNCTION  GetFolderName(vRefnum: INTEGER; VAR name: IEString): OSErr;
VAR	pb:		CInfoPBRec;
	temp:	Str255;
	err:	OSErr;
	dirID:	LONGINT;
	i:		INTEGER;
	len:	INTEGER;
		
BEGIN
name := '';
err := noErr;

IF gotHFS THEN { find the dirID, and get the full pathname }
	BEGIN
	err := GetDirID(vRefnum, dirID);
	
	IF err = noErr THEN
		GetFolderName := DirID2Name(vRefnum, dirID, name);
	END
ELSE { no HFS, just get the volume name }
	BEGIN
	temp := '';
	WITH pb DO
		BEGIN
		ioCompletion := NIL;
		ioNamePtr := @temp;
		ioVRefnum := vRefnum;
		ioFDirIndex := 0;
		ioDrDirID := 0;
		END;
		
	err := PBGetVInfo(@pb, FALSE);
	IF err = noErr THEN
		name := Concat(temp, ':');
	END;

GetFolderName := err;
END;


***** END OF CODE *****

-- 
Larry Rosenstein

Object Specialist
Apple Computer

AppleLink: Rosenstein1
UUCP:  {sun, voder, nsc, mtxinu, dual}!apple!lsr
CSNET: lsr@Apple.com

earleh@dartvax.UUCP (06/14/87)

In article <19339@ucbvax.BERKELEY.EDU>, oster@dewey.soe.berkeley.edu (David Phillip Oster) writes:
> 
> Many people have said they want to see how to work with complete
> pathnames to files expressed as strings. Please mention in your
> product literature that you are doing this - I won't buy any product
> that does.  

I agree!  Byte the bullet, programmers!  Use PBOpen, PBRead, PBWrite, and
the entire ensemble of IN ROM Macintosh file handling routines to make
your code more efficient and also more able to withstand the effects of
system upgrades.  You don't have to call SetVol(), you don't have to 
obtain the pathname of the file, and you don't have to worry about what
happens to your file connection when a disk gets ejected.  Of course,
this reduces "portability" but who are we kidding here?

A satisfied ROM user.

-- 
*********************************************************************
*Earle R. Horton, H.B. 8000, Dartmouth College, Hanover, NH 03755   *
*********************************************************************

clubmac@runx.ips.oz (Macintosh Users Group) (06/17/87)

In article <1345@batcomputer.tn.cornell.edu> steig@tcgould.tn.cornell.edu.UUCP (Mark J. Steiglitz) writes:
>
>I am writing a program in Lightspeed C that asks the user for a filename
>using the standard file package and then processes the text in that file
>using some of the stdio C routines.  I use fopen() to open the file.
>
>fopen() requires the pathname of the file, but SFGetFile and SFPutFile only
>return the filename and the working directory reference number.  How do I
>obtain the pathname of the file?
>-- 

Mark,
    I haven't seen any code to compute a full HFS pathname to a file from it's
    filename and WDRefNum (Does anyone have code?), but if you use GetVol
    and SetVol, you shouldn't have any problems.
	
    I hope this code segment helps.
	
	....
	GetVol(NULL,&currVol); /* Get default volume now for restoration after
				  fopen() call */
	SetVol(NULL,reply.vRefNum); /* Set default volume to volume abtained
					from SF{Get,Put}File call */
	PtoCstr(reply.fName);	/* Convert fName for fopen() call */
	fp = fopen(reply.fName,"r"); /* open */
	CtoPstr(reply.fName);		/* balance PtoCstr() */
	SetVol(NULL,currVol);		/* restore */
	....

Jason Haines

Club Mac Macintosh Users Group - Sydney, Australia
Snail:     Box 213, Holme Building, Sydney University, NSW, 2006, Australia
ACSnet:    clubmac@runx.ips.oz	   ARPA:   clubmac%runx.ips.oz@seismo.css.gov
UUCP:{enea,hplabs,mcvax,prlb2,seismo,ubc-vision,ukc}!munnari!runx.ips.oz!clubmac

rae@unicus.UUCP (Clith de T'nir a.k.a. Reid Ellis) (06/20/87)

In article <6462@dartvax.UUCP> earleh@dartvax.UUCP writes:
|
| ... Byte the bullet, programmers!  Use PBOpen, PBRead, PBWrite, and
| the entire ensemble of IN ROM Macintosh file handling routines ...
| ... you don't have to 
| obtain the pathname of the file, and you don't have to worry about what
| happens to your file connection when a disk gets ejected.  Of course,
| this reduces "portability" but who are we kidding here?

Um, doesn't this ignore applications where a path is read from a text
file? [along the lines of a C compiler's "#include <a:long:path>"]  What
then?  Make the user memorize vRefNums of his folders? :-)

							Reid
							+=--
---
					"ZOT, YOU JERK!"
						- Jenny
Reid Ellis, aka Clith de T'nir
		{seismo!mnetor, utzoo!yetti}!unicus!rae	(uucp)
		mnetor!unicus!rae@seismo.css.gov	(arpa)

earleh@dartvax.UUCP (Earle R. Horton) (06/22/87)

In article <665@unicus.UUCP>, rae@unicus.UUCP (Clith de T'nir a.k.a. 
        Reid Ellis) writes:
> In article <6462@dartvax.UUCP> earleh@dartvax.UUCP writes:
> |
> | ... Byte the bullet, programmers!  Use PBOpen, PBRead, PBWrite, and
> | the entire ensemble of IN ROM Macintosh file handling routines ...
> | ... you don't have to 
> | obtain the pathname of the file, and you don't have to worry about what
> | happens to your file connection when a disk gets ejected.  Of course,
> | this reduces "portability" but who are we kidding here?
> 
> Um, doesn't this ignore applications where a path is read from a text
> file? [along the lines of a C compiler's "#include <a:long:path>"]  What
> then?  Make the user memorize vRefNums of his folders? :-)

No, it does not.  A full pathname, if it is valid, will OVERRIDE an
explicit volume specification.  (IM, volume IV, File Manager chapter.)
If I were writing a C compiler, and wanted to handle this sort of
thing, this is exactly what I would have the program do:  

     a)  Set up a parameter block for dealing with the file
     b)  Put a zero in the ioVRefNum field
     c)  Put the pointer to the full pathname in the ioNamePtr field
     d)  Fill in ioCompletion, ioVersNum, ioMisc, ioPermssn as 
         appropriate
     e)  Call PBOpen to open the sucker.

Here is the main point:  At no point in this process does my program
have any knowledge of the format of the pathname.  I do not manipulate
the pathname, and I do not attempt to construct the full pathname if I
do not have it.  If the user obtains a file for me from one of the
Standard File Package routines, then the correct thing to do is, in
step b), just put in the vRefNum obtained from SFGetFile or whatever.
If the USER gives me a pathname, then I use it, as is.  Most programs
do not need to know the full pathname of a file, they just need a
RELIABLE means of gaining access to the file.  Using full pathnames on
the Mac is not a reliable means of obtaining access to a file, which
is why it should only be used in cases like Reid's example, above,
where it is absolutely necessary.

The information obtained from the Standard File Package routines is
sufficient to locate a file correctly on any Macintosh.  I do not see
why anyone would wish to translate this information into a format
which is NOT capable of always locating a file correctly.  This is
exactly what you do when you attempt to construct a full pathname to a
file.  This approach may be justified for some applications, but for
the enormous majority of applications that are to be used by ordinary
humans, it introduces the possibility of a run time error which is
simply not acceptable:

     a)  User finds a file name for program using SFPutFile, for
         purposes of saving whatever your program saves.
     b)  Program uses information in SFReply to fabricate a fully
         qualified pathname
     c)  Program does "fopen(pathname,"w");"
     d)  Macintosh knows of another online volume with the same name
         and with the same folder name as the one the user picked from 
         SFPutFile.  The pathname of this folder is identical to the 
         pathname of the folder the User wishes to use to save his
         current work in, with one exception:  It contains the only
         machine-readable copy of the User's PhD. thesis.  By unlucky
         coincidence, the two base file names are identical.
     e)  Macintosh says "I want the other disk."
     f)  User inserts other disk, not suspecting for a moment that he
         is dealing with a dog-meat program.
     g)  Current work is written over User's PhD. thesis.  Screams
         are heard.  A phone call is made to a powerful lawyer's 
	 office.
     h)  "By mistake" you have destroyed a valuable document, and 
         replaced it with something that was supposed to be placed
	 elsewhere.

Let us analyze the above scenario for a moment.  The programmer has been
too lazy to learn the correct way to access a file on the Macintosh.
Instead, he has chosen to use a method that is suited to more to UNIX,
VMS, and MS-DOS systems.  Results were not as expected.  The lazy 
programmer has made the Macintosh look bad, all because he failed to 
detect the difference between a Macintosh and another machine.  I 
leave you with a question:

Can you, members of the jury, think of this as an "honest mistake"?

-- 
*********************************************************************
*Earle R. Horton, H.B. 8000, Dartmouth College, Hanover, NH 03755   *
*********************************************************************

dwb@apple.UUCP (Dave W. Berry) (06/22/87)

In article <665@unicus.UUCP> rae@unicus.UUCP (Clith de T'nir a.k.a. Reid Ellis) writes:
>Um, doesn't this ignore applications where a path is read from a text
>file? [along the lines of a C compiler's "#include <a:long:path>"]  What
>then?  Make the user memorize vRefNums of his folders? :-)
	Rather difficult, since vRefNums are dynamically assigned
	as directories get opened.  And the correct thing to do
	is for the compiler to implement search paths so you don't
	have to give long full path names.  What happens if you
	rename the disk, or copy the whole structure to hard disk?
>
>							Reid
-- 
	David W. Berry
	dwb@well.uucp                   dwb@Delphi
	dwb@apple.com                   293-0752@408.MaBell

rae@unicus.UUCP (Clith de T'nir a.k.a. Reid Ellis) (06/23/87)

In article <1127@apple.UUCP> dwb@apple.UUCP (David W. Berry) writes:
|In article <665@unicus.UUCP> I write:
|>Um, doesn't this ignore applications where a path is read from a text
|>file? [along the lines of a C compiler's "#include <a:long:path>"]  What
|>then?  Make the user memorize vRefNums of his folders? :-)
|	... And the correct thing to do
|	is for the compiler to implement search paths so you don't
|	have to give long full path names.  What happens if you
|	rename the disk, or copy the whole structure to hard disk?
|-- 
|	David W. Berry
|	dwb@well.uucp                   dwb@Delphi
|	dwb@apple.com                   293-0752@408.MaBell

I would agree that one should not *force* a user to use full pathnames,
lest their fingers go numb :-), but what do you do when the user *does*
type a long path?  I wouldn't want to restrict her from doing so, so I must
support it in my application.  So my original point stands, What do you
do to support long path names?
  Larry at Apple recently posted some code that helps with this, and it is
muchly appreciated.  Thanks Larry.  Do you read Badger?

					Reid
					+=--

jimb@dopey.AMD.COM (Jim Budler) (06/25/87)

In article <6462@dartvax.UUCP) earleh@dartvax.UUCP (Earle R. Horton) writes:
)I agree!  Byte the bullet, programmers!  Use PBOpen, PBRead, PBWrite, and
)the entire ensemble of IN ROM Macintosh file handling routines to make
)your code more efficient and also more able to withstand the effects of
)system upgrades.  You don't have to call SetVol(), you don't have to 

It gives you something else, too. I wrote fpack for the Mac a year or so ago.
One condition that is very hard to detect with portable C is "DISK FULL"!
Corrupted output files galore...

How did I fix it? The very unelegant way of calling the ROM to find out how
much space I had left before I opened a file for writing, and keeping track
of how much I wrote. It worked, but I ain't proud of it.

fpack 3.x will come out one of these days, as will par/unpar. Both will use
the infinitely more responsive (i.e. PBWrite says "Disk Full", not "I just
wrote an 'a'") ROM IO calls.

By the way, it has come to my attention, through a friend, that fpack
is still circulating in the PD world, and that the copy he got (on his Jasmine)
didn't work. The copy on his Jasmine was version 2.0. I don't have the records
to show when the change came about, but version 2.2 is the earliest version
I can confirm was compiled with a version of Mac C which knows about HFS.

I believe the differences between 2.0, 2.1 and 2.2 are the compiler changes
and the version number resources.

Version 2.2 works on my Dataframe XP20.

Does anyone still care?
-- 
+      Jim Budler      Advanced Micro Devices, Inc.      (408) 749-5806      +
+  Compuserve: 72415,1200; Delphi: JIMBUDLER;  Usenet: jimb@amdcad.AMD.COM   +

dwb@apple.UUCP (Dave W. Berry) (06/30/87)

In article <684@unicus.UUCP> rae@unicus.UUCP (Clith de T'nir a.k.a. Reid Ellis) writes:
>In article <1127@apple.UUCP> dwb@apple.UUCP (David W. Berry) (me!) writes:
>|In article <665@unicus.UUCP> I write:
>|>Um, doesn't this ignore applications where a path is read from a text
>|>file? [along the lines of a C compiler's "#include <a:long:path>"]  What
>|>then?  Make the user memorize vRefNums of his folders? :-)
>|	... And the correct thing to do
>|	is for the compiler to implement search paths so you don't
>|	have to give long full path names.  What happens if you
>|	rename the disk, or copy the whole structure to hard disk?
>I would agree that one should not *force* a user to use full pathnames,
>lest their fingers go numb :-), but what do you do when the user *does*
>type a long path?  I wouldn't want to restrict her from doing so, so I must
>support it in my application.  So my original point stands, What do you
>do to support long path names?
	If you just pass the full path name to the open call you don't
	need to do anything special.
-- 
	David W. Berry
	dwb@well.uucp                   dwb@Delphi
	dwb@apple.com                   293-0752@408.MaBell