1 /*
2 	File:		FullPath.c
3 
4 	Contains:	Routines for dealing with full pathnames... if you really must.
5 
6 	Version:	MoreFiles
7 
8 	Copyright:	� 1995-2001 by Apple Computer, Inc., all rights reserved.
9 
10 	You may incorporate this sample code into your applications without
11 	restriction, though the sample code has been provided "AS IS" and the
12 	responsibility for its operation is 100% yours.  However, what you are
13 	not permitted to do is to redistribute the source as "DSC Sample Code"
14 	after having made changes. If you're going to re-distribute the source,
15 	we require that you make it clear in the source that the code was
16 	descended from Apple Sample Code, but that you've made changes.
17 
18 	File Ownership:
19 
20 		DRI:				Apple Macintosh Developer Technical Support
21 
22 		Other Contact:		Apple Macintosh Developer Technical Support
23 							<http://developer.apple.com/bugreporter/>
24 
25 		Technology:			DTS Sample Code
26 
27 	Writers:
28 
29 		(JL)	Jim Luther
30 
31 	Change History (most recent first):
32 
33 		 <2>	  2/7/01	JL		Added standard header. Updated names of includes.
34 		<1>		12/06/99	JL		MoreFiles 1.5.
35 */
36 
37 /*
38      This file has been slightly modified from the original by
39      the National Center for Biotechnology Information, National
40      Institutes of Health, Bethesda, Maryland, USA.
41 */
42 
43 /* C++ style comments converted to C comments by NCBI */
44 
45 
46 #ifndef WIN32
47 
48 #include <MacTypes.h>
49 #include <MacErrors.h>
50 #include <MacMemory.h>
51 #include <Files.h>
52 #include <TextUtils.h>
53 #include <Aliases.h>
54 
55 #define	__COMPILINGMOREFILES
56 
57 #include "FSpCompat.h"
58 #include "FullPath.h"
59 
60 /*
61 	IMPORTANT NOTE:
62 
63 	The use of full pathnames is strongly discouraged. Full pathnames are
64 	particularly unreliable as a means of identifying files, directories
65 	or volumes within your application, for two primary reasons:
66 
67 	� 	The user can change the name of any element in the path at virtually
68 		any time.
69 	�	Volume names on the Macintosh are *not* unique. Multiple
70 		mounted volumes can have the same name. For this reason, the use of
71 		a full pathname to identify a specific volume may not produce the
72 		results you expect. If more than one volume has the same name and
73 		a full pathname is used, the File Manager currently uses the first
74 		mounted volume it finds with a matching name in the volume queue.
75 
76 	In general, you should use a file�s name, parent directory ID, and
77 	volume reference number to identify a file you want to open, delete,
78 	or otherwise manipulate.
79 
80 	If you need to remember the location of a particular file across
81 	subsequent system boots, use the Alias Manager to create an alias record
82 	describing the file. If the Alias Manager is not available, you can save
83 	the file�s name, its parent directory ID, and the name of the volume on
84 	which it�s located. Although none of these methods is foolproof, they are
85 	much more reliable than using full pathnames to identify files.
86 
87 	Nonetheless, it is sometimes useful to display a file�s full pathname to
88 	the user. For example, a backup utility might display a list of full
89 	pathnames of files as it copies them onto the backup medium. Or, a
90 	utility might want to display a dialog box showing the full pathname of
91 	a file when it needs the user�s confirmation to delete the file. No
92 	matter how unreliable full pathnames may be from a file-specification
93 	viewpoint, users understand them more readily than volume reference
94 	numbers or directory IDs. (Hint: Use the TruncString function from
95 	TextUtils.h with truncMiddle as the truncWhere argument to shorten
96 	full pathnames to a displayable length.)
97 
98 	The following technique for constructing the full pathname of a file is
99 	intended for display purposes only. Applications that depend on any
100 	particular structure of a full pathname are likely to fail on alternate
101 	foreign file systems or under future system software versions.
102 */
103 
104 /*****************************************************************************/
105 
GetFullPath(short vRefNum,long dirID,ConstStr255Param name,short * fullPathLength,Handle * fullPath)106 pascal	OSErr	GetFullPath(short vRefNum,
107 							long dirID,
108 							ConstStr255Param name,
109 							short *fullPathLength,
110 							Handle *fullPath)
111 {
112 	OSErr		result;
113 	FSSpec		spec;
114 
115 	*fullPathLength = 0;
116 	*fullPath = NULL;
117 
118 	result = FSMakeFSSpecCompat(vRefNum, dirID, name, &spec);
119 	if ( (result == noErr) || (result == fnfErr) )
120 	{
121 		result = FSpGetFullPath(&spec, fullPathLength, fullPath);
122 	}
123 
124 	return ( result );
125 }
126 
127 /*****************************************************************************/
128 
FSpGetFullPath(const FSSpec * spec,short * fullPathLength,Handle * fullPath)129 pascal	OSErr	FSpGetFullPath(const FSSpec *spec,
130 							   short *fullPathLength,
131 							   Handle *fullPath)
132 {
133 	OSErr		result;
134 	OSErr		realResult;
135 	FSSpec		tempSpec;
136 	CInfoPBRec	pb;
137 
138 	*fullPathLength = 0;
139 	*fullPath = NULL;
140 
141 
142 	/* Default to noErr */
143 	realResult = result = noErr;
144 
145 	/* work around Nav Services "bug" (it returns invalid FSSpecs with empty names) */
146 	if ( spec->name[0] == 0 )
147 	{
148 		result = FSMakeFSSpecCompat(spec->vRefNum, spec->parID, spec->name, &tempSpec);
149 	}
150 	else
151 	{
152 		/* Make a copy of the input FSSpec that can be modified */
153 		BlockMoveData(spec, &tempSpec, sizeof(FSSpec));
154 	}
155 
156 	if ( result == noErr )
157 	{
158 		if ( tempSpec.parID == fsRtParID )
159 		{
160 			/* The object is a volume */
161 
162 			/* Add a colon to make it a full pathname */
163 			++tempSpec.name[0];
164 			tempSpec.name[tempSpec.name[0]] = ':';
165 
166 			/* We're done */
167 			result = PtrToHand(&tempSpec.name[1], fullPath, tempSpec.name[0]);
168 		}
169 		else
170 		{
171 			/* The object isn't a volume */
172 
173 			/* Is the object a file or a directory? */
174 			pb.dirInfo.ioNamePtr = tempSpec.name;
175 			pb.dirInfo.ioVRefNum = tempSpec.vRefNum;
176 			pb.dirInfo.ioDrDirID = tempSpec.parID;
177 			pb.dirInfo.ioFDirIndex = 0;
178 			result = PBGetCatInfoSync(&pb);
179 			/* Allow file/directory name at end of path to not exist. */
180 			realResult = result;
181 			if ( (result == noErr) || (result == fnfErr) )
182 			{
183 				/* if the object is a directory, append a colon so full pathname ends with colon */
184 				if ( (result == noErr) && (pb.hFileInfo.ioFlAttrib & kioFlAttribDirMask) != 0 )
185 				{
186 					++tempSpec.name[0];
187 					tempSpec.name[tempSpec.name[0]] = ':';
188 				}
189 
190 				/* Put the object name in first */
191 				result = PtrToHand(&tempSpec.name[1], fullPath, tempSpec.name[0]);
192 				if ( result == noErr )
193 				{
194 					/* Get the ancestor directory names */
195 					pb.dirInfo.ioNamePtr = tempSpec.name;
196 					pb.dirInfo.ioVRefNum = tempSpec.vRefNum;
197 					pb.dirInfo.ioDrParID = tempSpec.parID;
198 					do	/* loop until we have an error or find the root directory */
199 					{
200 						pb.dirInfo.ioFDirIndex = -1;
201 						pb.dirInfo.ioDrDirID = pb.dirInfo.ioDrParID;
202 						result = PBGetCatInfoSync(&pb);
203 						if ( result == noErr )
204 						{
205 							/* Append colon to directory name */
206 							++tempSpec.name[0];
207 							tempSpec.name[tempSpec.name[0]] = ':';
208 
209 							/* Add directory name to beginning of fullPath */
210 							(void) Munger(*fullPath, 0, NULL, 0, &tempSpec.name[1], tempSpec.name[0]);
211 							result = MemError();
212 						}
213 					} while ( (result == noErr) && (pb.dirInfo.ioDrDirID != fsRtDirID) );
214 				}
215 			}
216 		}
217 	}
218 
219 	if ( result == noErr )
220 	{
221 		/* Return the length */
222 		*fullPathLength = GetHandleSize(*fullPath);
223 		result = realResult;	/* return realResult in case it was fnfErr */
224 	}
225 	else
226 	{
227 		/* Dispose of the handle and return NULL and zero length */
228 		if ( *fullPath != NULL )
229 		{
230 			DisposeHandle(*fullPath);
231 		}
232 		*fullPath = NULL;
233 		*fullPathLength = 0;
234 	}
235 
236 	return ( result );
237 }
238 
239 /*****************************************************************************/
240 
FSpLocationFromFullPath(short fullPathLength,const void * fullPath,FSSpec * spec)241 pascal OSErr FSpLocationFromFullPath(short fullPathLength,
242 									 const void *fullPath,
243 									 FSSpec *spec)
244 {
245 	AliasHandle	alias;
246 	OSErr		result;
247 	Boolean		wasChanged;
248 	Str32		nullString;
249 
250 	/* Create a minimal alias from the full pathname */
251 	nullString[0] = 0;	/* null string to indicate no zone or server name */
252 	result = NewAliasMinimalFromFullPath(fullPathLength, fullPath, nullString, nullString, &alias);
253 	if ( result == noErr )
254 	{
255 		/* Let the Alias Manager resolve the alias. */
256 		result = ResolveAlias(NULL, alias, spec, &wasChanged);
257 
258 		/* work around Alias Mgr sloppy volume matching bug */
259 		if ( spec->vRefNum == 0 )
260 		{
261 			/* invalidate wrong FSSpec */
262 			spec->parID = 0;
263 			spec->name[0] =  0;
264 			result = nsvErr;
265 		}
266 		DisposeHandle((Handle)alias);	/* Free up memory used */
267 	}
268 	return ( result );
269 }
270 
271 /*****************************************************************************/
272 
LocationFromFullPath(short fullPathLength,const void * fullPath,short * vRefNum,long * parID,Str31 name)273 pascal OSErr LocationFromFullPath(short fullPathLength,
274 								  const void *fullPath,
275 								  short *vRefNum,
276 								  long *parID,
277 								  Str31 name)
278 {
279 	OSErr	result;
280 	FSSpec	spec;
281 
282 	result = FSpLocationFromFullPath(fullPathLength, fullPath, &spec);
283 	if ( result == noErr )
284 	{
285 		*vRefNum = spec.vRefNum;
286 		*parID = spec.parID;
287 		BlockMoveData(&spec.name[0], &name[0], spec.name[0] + 1);
288 	}
289 	return ( result );
290 }
291 
292 /*****************************************************************************/
293 
294 #endif /* WIN32 */
295