[comp.sys.mac.programmer] Full path name of a file

olausson@chalmers.UUCP (Stefan Olausson) (05/04/88)

Given the information in a SFReply, how do I construct the full path name
of a file (as a string), i e "disk:dir1:dir2:...:filename" ?

  Stefan Olausson
=========================================================================
  E-mail: olausson@cs.chalmers.se

guido@cwi.nl (Guido van Rossum) (05/06/88)

In article <2532@chalmers.UUCP> olausson@chalmers.UUCP (Stefan Olausson) writes:
>Given the information in a SFReply, how do I construct the full path name
>of a file (as a string), i e "disk:dir1:dir2:...:filename" ?

Here's my code.  I am not posting the header file "macdefs.h" or the
"dprintf" routine but you should be able to gues what's in them.; or ask
me to mail them.  This was tested with MPW.

/* GET FULL PATHNAME OF A FILE.
   Public Domain by Guido van Rossum, CWI, Amsterdam. May 1988. */

#include "macwin.h"

/* Mac file system parameters */
#define MAXPATH 256	/* Max path name length+1 */
#define SEP ':'		/* Separator in path names */
#define ROOTID 2	/* DirID of a volume's root directory */

/* Macro to find out whether we can do HFS-only calls: */
#define FSFCBLen (* (short *) 0x3f6)
#define hfsrunning() (FSFCBLen > 0)

char *
getdirname(dir)
	long dir; /* WDRefNum */
{
	union {
		HFileInfo f;
		DirInfo d;
		WDPBRec w;
		VolumeParam v;
	} pb;
	static char cwd[2*MAXPATH];
	char namebuf[MAXPATH];
	short err;
	long dirid= 0;
	char *next= cwd + sizeof cwd - 1;
	int len;
	
	if (!hfsrunning())
		return "";
	
	for (;;) {
		pb.d.ioNamePtr= namebuf;
		pb.d.ioVRefNum= dir;
		pb.d.ioFDirIndex= -1;
		pb.d.ioDrDirID= dirid;
		err= PBGetCatInfo(&pb.d, FALSE);
		if (err != noErr) {
			dprintf("PBCatInfo err %d", err);
			return NULL;
		}
		*--next= SEP;
		len= namebuf[0];
		/* There is no overflow check on cwd here! */
		strncpy(next -= len, namebuf+1, len);
		if (pb.d.ioDrDirID == ROOTID)
			break;
		dirid= pb.d.ioDrParID;
	}
	return next;
}

void
fullpath(buf, wdrefnum, file)
	char *buf;
	int wdrefnum;
	char *file;
{
	strcpy(buf, getdirname(wdrefnum));
	strcat(buf, file);
}

--
Guido van Rossum, Centre for Mathematics and Computer Science (CWI), Amsterdam
guido@piring.cwi.nl or mcvax!piring!guido or guido%piring.cwi.nl@uunet.uu.net

dorourke@polyslo.UUCP (David M. O'Rourke) (05/07/88)

In article <2532@chalmers.UUCP> olausson@chalmers.UUCP (Stefan Olausson) writes:
>Given the information in a SFReply, how do I construct the full path name
>of a file (as a string), i e "disk:dir1:dir2:...:filename" ?

  I'm not sure which one, if any, of the following tech notes gives you
the information necessary.  I think you need to read all of them, and then
build a composit tech note yourself.

    Tech Note 68 Searching all directories on an HFS volume.
    Tech Note 69 Setting ioFDirIndex in PBGetCatInfo Calls
    Tech Note 107 Nulls in FileNames.

  Some of these tech notes might help.  I've been wanting to do the same thing
for a long time, but haven't had the time to wade thru the information.

David M. O'Rourke

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| dorourke@polyslo | Disclaimer:  All opinions in this message are mine, but  |
|                  |              if you like them they can be yours too.     |
|                  |              Besides I'm just a student so what do I     |
|                  |              know!                                       |
|-----------------------------------------------------------------------------|
|    When you have to place a disclaimer in your mail you know it's a sign    |
| that there are TOO many Lawyer's.                                           |
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

km@cadre.dsl.PITTSBURGH.EDU (Ken Mitchum) (05/07/88)

In article <2532@chalmers.UUCP> olausson@chalmers.UUCP (Stefan Olausson) writes:
>Given the information in a SFReply, how do I construct the full path name
>of a file (as a string), i e "disk:dir1:dir2:...:filename" ?

Be certain that you understand the difference between "working directory
reference numbers", which SFReply returns, and directory ID numbers, which
you will use to find the path name. (I only had to read the file manager
chapter in Inside Mac Volume IV about ten times before understanding this!).

Basically, you want to know the "real" volume (NOT the working directory
reference number) and the "real" directory ID, which are two different things,
neither of which being the working directory reference number. While you can
take the "working directory reference number" from the SFReply structure and
convert it into both volume and directory ID numbers, it is easier to take
advantage of two global variables, CurDirStore and SFSaveDisk (see insert
on page 72 of Inside Macintosh Vol IV). After any of the SF routines, these
global variables are set as follows, REGARDLESS OF WHETHER THE USER CHOSE
A FILE OR CANCELLED:

  CurDirStore = directoryID (long int)
  SFSaveDisk = 0 - volume (int)

Thus, if the user changed directories but cancelled the operation, the above
variables are set the way the user left them (to the new directory). It is
convenient to keep track of these separately. The following code from MacJove
shows a simple way to recover the path using these. In this case, the
current directory is stored in  cur_dir and current volume in cur_vol, which
are set elsewhere (cur_dir - CurDirStore; cur_vol = 0 - SFSaveDisk. (This
routine returns a "unix-style" path rather than a Mac-style path, using
"/").

char *getwd()
{
	DirInfo d;
	static char ret[255];
	char nm[50], tmp[255];
	
	ret[0] = '\0';
	d.ioDrDirID = cur_dir;
	for(;;) {
		d.ioCompletion = 0;
		d.ioNamePtr = (StringPtr) nm;
		d.ioVRefNum = cur_vol;
		d.ioFDirIndex = -1;

		PBGetCatInfo(&d,0);
		if(d.ioResult != noErr) return(0);
		PtoCstr((char *) nm);
		strcpy(tmp,ret);
		strcpy(ret,"/");
		strcat(ret,nm);
		strcat(ret,tmp);
		if(d.ioDrDirID == 2) break;	/* home directory */
		d.ioDrDirID = d.ioDrParID;
	} 
	return(ret);
}

 -Ken Mitchum
 Decision Systems Laboratories
 University of Pittsburgh
 km@cadre.dsl.pittsburgh.edu

guido@cwi.nl (Guido van Rossum) (05/08/88)

I posted a routine to get the full pathname of a file.  It had been
working for some months.  But it still contained a bug, which became
apparent when I switched from MPW to LightspeedC this weekend.  The
parameter to getdirname should be an int, not a long!

Also I can now tell you what include files you need for LSC:
	#include <HFS.h>
	#define NULL 0

Note that it is perfectly legal for a Macintosh owner to create a
directory hierarchy where the length of full path names of the deepest
files exceeds 255 bytes; since the file system never manipulates full
pathnames internally (it only ever sees them when passed as parameters),
it doesn't and needn't check.  However, this poses an ethical problem if
you are constructing full pathnames: my code simply bombs if it would
construct a pathname >255 bytes, and if I increased the buffer size, the
resulting pathnames are useless except for documentation purposes (since
the file system can't have string parameters >255 bytes).
--
Guido van Rossum, Centre for Mathematics and Computer Science (CWI), Amsterdam
guido@piring.cwi.nl or mcvax!piring!guido or guido%piring.cwi.nl@uunet.uu.net

wetter@tybalt.caltech.edu (Pierce T. Wetter) (05/09/88)

>it doesn't and needn't check.  However, this poses an ethical problem if
>you are constructing full pathnames: my code simply bombs if it would
>construct a pathname >255 bytes, and if I increased the buffer size, the
>resulting pathnames are useless except for documentation purposes (since
>the file system can't have string parameters >255 bytes).

  I'm assuming you have some special file your application needs to look at
which for some reason you don't want to save with your application in the
data fork. You want this file to be located somewhere other then the same
folder as the application or the system folder.
  
   Two ways to do this: If you can't find the file put up an sfgetfile box
and ask the user where it is. Then save the pathname in a str resource. If its
longer then 255 bytes, your application will just keep making the user find
it each time.

  Or, if the pathname is longer then 255 bytes, save it in pieces and wander
down the heiarchy each time you need to find the file.

Pierce Wetter.


----------------------------------------------------------------
wetter@tybalt.caltech.edu     Race For Space Grand Prize Winner.
-----------------------------------------------------------------
   Useless Advice #986: Never sit on a Tack.

guido@cwi.nl (Guido van Rossum) (05/10/88)

In article <6464@cit-vax.Caltech.Edu> wetter@tybalt.caltech.edu.UUCP
(Pierce T. Wetter) writes:
>[replying to what I wrote about full path names >255 bytes:]
>  I'm assuming you have some special file your application needs to look at
>which for some reason you don't want to save with your application in the
>data fork. You want this file to be located somewhere other then the same
>folder as the application or the system folder.
>[tricks how to do it right omitted]

In fact I was just replying to somebody asking "how do I compute the
full path name of a file?".  God knows why they need it.  I know
several programs which put the full path name of a file in the window
title when editing it (however they usually replace the middle with
"..." if it becomes too large).

Personally, I use the code because I am porting non-Mac programs to the
Mac; these programs ask the user for a file name and then open it using
fopen(3).  I replace the piece of code that asks for a file name with a
routine call which uses SF{Get,Put}File and converts the file found to
an absolute path name.  I can't return the relative path name and set
the default directory because the program will remember the file name
for a very long time.

--
Guido van Rossum, Centre for Mathematics and Computer Science (CWI), Amsterdam
guido@piring.cwi.nl or mcvax!piring!guido or guido%piring.cwi.nl@uunet.uu.net

darin@Apple.COM (Darin Adler) (05/10/88)

In article <6464@cit-vax.Caltech.Edu> wetter@tybalt.caltech.edu.UUCP (Pierce T. Wetter) writes:
>   I'm assuming you have some special file your application needs to look at
> which for some reason you don't want to save with your application in the
> data fork. You want this file to be located somewhere other then the same
> folder as the application or the system folder.
>   
>    Two ways to do this: If you can't find the file put up an sfgetfile box
> and ask the user where it is. Then save the pathname in a str resource. If its
> longer then 255 bytes, your application will just keep making the user find
> it each time.
> 
>   Or, if the pathname is longer then 255 bytes, save it in pieces and wander
> down the heiarchy each time you need to find the file.

Developer Technical Support recommends this solution:

	Save the volume name, dirID of the directory containing the
	file, and file name. If the file is not where you expect it,
	use SFGetFile to have the user find it.

This has a number of advantages. First of all, dirIDs are guaranteed to be
unique for a volume. No dirID is ever reused. DirIDs also remain the same,
even if one of the directories in the path down to the file is renamed.

I can't really think of ANY good reason to calculate the full path name, except
perhaps to display on the screen for some advanced users.
-- 
Darin Adler						AppleLink:Adler4
UUCP: {sun,voder,nsc,mtxinu,dual}!apple!darin	  CSNET: darin@Apple.com

lsr@Apple.COM (Larry Rosenstein) (05/17/88)

In article <6464@cit-vax.Caltech.Edu> wetter@tybalt.caltech.edu.UUCP (Pierce T. Wetter) writes:
>  
>   Two ways to do this: If you can't find the file put up an sfgetfile box
>and ask the user where it is. Then save the pathname in a str resource. If its
>longer then 255 bytes, your application will just keep making the user find
>it each time.

The problem with saving a pathname is that it is very easy for the user to
change one folder along the path.  An alternative is to save the dirID of
the folder.  The dirID is unique and won't change unless the disk is
reformatted, which is hopefully less often than a folder changes name.  If
you can't find the file, then put up the sfgetfile box.  (The program might
first look in some standard places such as the same folder as the
application and the system folder.)

(I have heard that some implementations of AppleTalk Filing Protocol on
non-Macs don't save unique dirIDs on the server.  They create dirIDs as
needed, and these are only valid as long as you are logged in.  If this is
true, then my idea won't work well to locate files on those kinds of
servers.) -- 
		 Larry Rosenstein,  Object Specialist
 Apple Computer, Inc.  20525 Mariani Ave, MS 27-AJ  Cupertino, CA 95014
	    AppleLink:Rosenstein1    domain:lsr@Apple.COM
		UUCP:{sun,voder,nsc,decwrl}!apple!lsr