1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd.
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * All rights reserved.
5*7c478bd9Sstevel@tonic-gate  *
6*7c478bd9Sstevel@tonic-gate  * Permission is hereby granted, free of charge, to any person obtaining a
7*7c478bd9Sstevel@tonic-gate  * copy of this software and associated documentation files (the
8*7c478bd9Sstevel@tonic-gate  * "Software"), to deal in the Software without restriction, including
9*7c478bd9Sstevel@tonic-gate  * without limitation the rights to use, copy, modify, merge, publish,
10*7c478bd9Sstevel@tonic-gate  * distribute, and/or sell copies of the Software, and to permit persons
11*7c478bd9Sstevel@tonic-gate  * to whom the Software is furnished to do so, provided that the above
12*7c478bd9Sstevel@tonic-gate  * copyright notice(s) and this permission notice appear in all copies of
13*7c478bd9Sstevel@tonic-gate  * the Software and that both the above copyright notice(s) and this
14*7c478bd9Sstevel@tonic-gate  * permission notice appear in supporting documentation.
15*7c478bd9Sstevel@tonic-gate  *
16*7c478bd9Sstevel@tonic-gate  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17*7c478bd9Sstevel@tonic-gate  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18*7c478bd9Sstevel@tonic-gate  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
19*7c478bd9Sstevel@tonic-gate  * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
20*7c478bd9Sstevel@tonic-gate  * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
21*7c478bd9Sstevel@tonic-gate  * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
22*7c478bd9Sstevel@tonic-gate  * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
23*7c478bd9Sstevel@tonic-gate  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
24*7c478bd9Sstevel@tonic-gate  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25*7c478bd9Sstevel@tonic-gate  *
26*7c478bd9Sstevel@tonic-gate  * Except as contained in this notice, the name of a copyright holder
27*7c478bd9Sstevel@tonic-gate  * shall not be used in advertising or otherwise to promote the sale, use
28*7c478bd9Sstevel@tonic-gate  * or other dealings in this Software without prior written authorization
29*7c478bd9Sstevel@tonic-gate  * of the copyright holder.
30*7c478bd9Sstevel@tonic-gate  */
31*7c478bd9Sstevel@tonic-gate 
32*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
33*7c478bd9Sstevel@tonic-gate 
34*7c478bd9Sstevel@tonic-gate /*
35*7c478bd9Sstevel@tonic-gate  * If file-system access is to be excluded, this module has no function,
36*7c478bd9Sstevel@tonic-gate  * so all of its code should be excluded.
37*7c478bd9Sstevel@tonic-gate  */
38*7c478bd9Sstevel@tonic-gate #ifndef WITHOUT_FILE_SYSTEM
39*7c478bd9Sstevel@tonic-gate 
40*7c478bd9Sstevel@tonic-gate #include <stdio.h>
41*7c478bd9Sstevel@tonic-gate #include <stdlib.h>
42*7c478bd9Sstevel@tonic-gate #include <errno.h>
43*7c478bd9Sstevel@tonic-gate #include <string.h>
44*7c478bd9Sstevel@tonic-gate #include <ctype.h>
45*7c478bd9Sstevel@tonic-gate #include <limits.h>
46*7c478bd9Sstevel@tonic-gate 
47*7c478bd9Sstevel@tonic-gate #include <unistd.h>
48*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
49*7c478bd9Sstevel@tonic-gate #include <sys/stat.h>
50*7c478bd9Sstevel@tonic-gate 
51*7c478bd9Sstevel@tonic-gate #include "pathutil.h"
52*7c478bd9Sstevel@tonic-gate 
53*7c478bd9Sstevel@tonic-gate /*.......................................................................
54*7c478bd9Sstevel@tonic-gate  * Create a new PathName object.
55*7c478bd9Sstevel@tonic-gate  *
56*7c478bd9Sstevel@tonic-gate  * Output:
57*7c478bd9Sstevel@tonic-gate  *  return  PathName *  The new object, or NULL on error.
58*7c478bd9Sstevel@tonic-gate  */
59*7c478bd9Sstevel@tonic-gate PathName *_new_PathName(void)
60*7c478bd9Sstevel@tonic-gate {
61*7c478bd9Sstevel@tonic-gate   PathName *path;  /* The object to be returned */
62*7c478bd9Sstevel@tonic-gate /*
63*7c478bd9Sstevel@tonic-gate  * Allocate the container.
64*7c478bd9Sstevel@tonic-gate  */
65*7c478bd9Sstevel@tonic-gate   path = (PathName *) malloc(sizeof(PathName));
66*7c478bd9Sstevel@tonic-gate   if(!path) {
67*7c478bd9Sstevel@tonic-gate     errno = ENOMEM;
68*7c478bd9Sstevel@tonic-gate     return NULL;
69*7c478bd9Sstevel@tonic-gate   };
70*7c478bd9Sstevel@tonic-gate /*
71*7c478bd9Sstevel@tonic-gate  * Before attempting any operation that might fail, initialize the
72*7c478bd9Sstevel@tonic-gate  * container at least up to the point at which it can safely be passed
73*7c478bd9Sstevel@tonic-gate  * to _del_PathName().
74*7c478bd9Sstevel@tonic-gate  */
75*7c478bd9Sstevel@tonic-gate   path->name = NULL;
76*7c478bd9Sstevel@tonic-gate   path->dim = 0;
77*7c478bd9Sstevel@tonic-gate /*
78*7c478bd9Sstevel@tonic-gate  * Figure out the maximum length of an expanded pathname.
79*7c478bd9Sstevel@tonic-gate  */
80*7c478bd9Sstevel@tonic-gate   path->dim = _pu_pathname_dim();
81*7c478bd9Sstevel@tonic-gate   if(path->dim == 0)
82*7c478bd9Sstevel@tonic-gate     return _del_PathName(path);
83*7c478bd9Sstevel@tonic-gate /*
84*7c478bd9Sstevel@tonic-gate  * Allocate the pathname buffer.
85*7c478bd9Sstevel@tonic-gate  */
86*7c478bd9Sstevel@tonic-gate   path->name = (char *)malloc(path->dim * sizeof(char));
87*7c478bd9Sstevel@tonic-gate   if(!path->name) {
88*7c478bd9Sstevel@tonic-gate     errno = ENOMEM;
89*7c478bd9Sstevel@tonic-gate     return _del_PathName(path);
90*7c478bd9Sstevel@tonic-gate   };
91*7c478bd9Sstevel@tonic-gate   return path;
92*7c478bd9Sstevel@tonic-gate }
93*7c478bd9Sstevel@tonic-gate 
94*7c478bd9Sstevel@tonic-gate /*.......................................................................
95*7c478bd9Sstevel@tonic-gate  * Delete a PathName object.
96*7c478bd9Sstevel@tonic-gate  *
97*7c478bd9Sstevel@tonic-gate  * Input:
98*7c478bd9Sstevel@tonic-gate  *  path   PathName *  The object to be deleted.
99*7c478bd9Sstevel@tonic-gate  * Output:
100*7c478bd9Sstevel@tonic-gate  *  return PathName *  The deleted object (always NULL).
101*7c478bd9Sstevel@tonic-gate  */
102*7c478bd9Sstevel@tonic-gate PathName *_del_PathName(PathName *path)
103*7c478bd9Sstevel@tonic-gate {
104*7c478bd9Sstevel@tonic-gate   if(path) {
105*7c478bd9Sstevel@tonic-gate     if(path->name)
106*7c478bd9Sstevel@tonic-gate       free(path->name);
107*7c478bd9Sstevel@tonic-gate     free(path);
108*7c478bd9Sstevel@tonic-gate   };
109*7c478bd9Sstevel@tonic-gate   return NULL;
110*7c478bd9Sstevel@tonic-gate }
111*7c478bd9Sstevel@tonic-gate 
112*7c478bd9Sstevel@tonic-gate /*.......................................................................
113*7c478bd9Sstevel@tonic-gate  * Return the pathname to a zero-length string.
114*7c478bd9Sstevel@tonic-gate  *
115*7c478bd9Sstevel@tonic-gate  * Input:
116*7c478bd9Sstevel@tonic-gate  *  path     PathName *  The pathname container.
117*7c478bd9Sstevel@tonic-gate  * Output:
118*7c478bd9Sstevel@tonic-gate  *  return       char *  The cleared pathname buffer, or NULL on error.
119*7c478bd9Sstevel@tonic-gate  */
120*7c478bd9Sstevel@tonic-gate char *_pn_clear_path(PathName *path)
121*7c478bd9Sstevel@tonic-gate {
122*7c478bd9Sstevel@tonic-gate /*
123*7c478bd9Sstevel@tonic-gate  * Check the arguments.
124*7c478bd9Sstevel@tonic-gate  */
125*7c478bd9Sstevel@tonic-gate   if(!path) {
126*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
127*7c478bd9Sstevel@tonic-gate     return NULL;
128*7c478bd9Sstevel@tonic-gate   };
129*7c478bd9Sstevel@tonic-gate   path->name[0] = '\0';
130*7c478bd9Sstevel@tonic-gate   return path->name;
131*7c478bd9Sstevel@tonic-gate }
132*7c478bd9Sstevel@tonic-gate 
133*7c478bd9Sstevel@tonic-gate /*.......................................................................
134*7c478bd9Sstevel@tonic-gate  * Append a string to a pathname, increasing the size of the pathname
135*7c478bd9Sstevel@tonic-gate  * buffer if needed.
136*7c478bd9Sstevel@tonic-gate  *
137*7c478bd9Sstevel@tonic-gate  * Input:
138*7c478bd9Sstevel@tonic-gate  *  path        PathName *  The pathname container.
139*7c478bd9Sstevel@tonic-gate  *  string    const char *  The string to be appended to the pathname.
140*7c478bd9Sstevel@tonic-gate  *                          Note that regardless of the slen argument,
141*7c478bd9Sstevel@tonic-gate  *                          this should be a '\0' terminated string.
142*7c478bd9Sstevel@tonic-gate  *  slen             int    The maximum number of characters to append
143*7c478bd9Sstevel@tonic-gate  *                          from string[], or -1 to append the whole
144*7c478bd9Sstevel@tonic-gate  *                          string.
145*7c478bd9Sstevel@tonic-gate  *  remove_escapes   int    If true, remove the backslashes that escape
146*7c478bd9Sstevel@tonic-gate  *                          spaces, tabs, backslashes etc..
147*7c478bd9Sstevel@tonic-gate  * Output:
148*7c478bd9Sstevel@tonic-gate  *  return          char *  The pathname string path->name[], which may
149*7c478bd9Sstevel@tonic-gate  *                          have been reallocated, or NULL if there was
150*7c478bd9Sstevel@tonic-gate  *                          insufficient memory to extend the pathname.
151*7c478bd9Sstevel@tonic-gate  */
152*7c478bd9Sstevel@tonic-gate char *_pn_append_to_path(PathName *path, const char *string, int slen,
153*7c478bd9Sstevel@tonic-gate 			int remove_escapes)
154*7c478bd9Sstevel@tonic-gate {
155*7c478bd9Sstevel@tonic-gate   int pathlen;     /* The length of the pathname */
156*7c478bd9Sstevel@tonic-gate   int i;
157*7c478bd9Sstevel@tonic-gate /*
158*7c478bd9Sstevel@tonic-gate  * Check the arguments.
159*7c478bd9Sstevel@tonic-gate  */
160*7c478bd9Sstevel@tonic-gate   if(!path || !string) {
161*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
162*7c478bd9Sstevel@tonic-gate     return NULL;
163*7c478bd9Sstevel@tonic-gate   };
164*7c478bd9Sstevel@tonic-gate /*
165*7c478bd9Sstevel@tonic-gate  * Get the current length of the pathname.
166*7c478bd9Sstevel@tonic-gate  */
167*7c478bd9Sstevel@tonic-gate   pathlen = strlen(path->name);
168*7c478bd9Sstevel@tonic-gate /*
169*7c478bd9Sstevel@tonic-gate  * How many characters should be appended?
170*7c478bd9Sstevel@tonic-gate  */
171*7c478bd9Sstevel@tonic-gate   if(slen < 0 || slen > strlen(string))
172*7c478bd9Sstevel@tonic-gate     slen = strlen(string);
173*7c478bd9Sstevel@tonic-gate /*
174*7c478bd9Sstevel@tonic-gate  * Resize the pathname if needed.
175*7c478bd9Sstevel@tonic-gate  */
176*7c478bd9Sstevel@tonic-gate   if(!_pn_resize_path(path, pathlen + slen))
177*7c478bd9Sstevel@tonic-gate     return NULL;
178*7c478bd9Sstevel@tonic-gate /*
179*7c478bd9Sstevel@tonic-gate  * Append the string to the output pathname, removing any escape
180*7c478bd9Sstevel@tonic-gate  * characters found therein.
181*7c478bd9Sstevel@tonic-gate  */
182*7c478bd9Sstevel@tonic-gate   if(remove_escapes) {
183*7c478bd9Sstevel@tonic-gate     int is_escape = 0;
184*7c478bd9Sstevel@tonic-gate     for(i=0; i<slen; i++) {
185*7c478bd9Sstevel@tonic-gate       is_escape = !is_escape && string[i] == '\\';
186*7c478bd9Sstevel@tonic-gate       if(!is_escape)
187*7c478bd9Sstevel@tonic-gate 	path->name[pathlen++] = string[i];
188*7c478bd9Sstevel@tonic-gate     };
189*7c478bd9Sstevel@tonic-gate /*
190*7c478bd9Sstevel@tonic-gate  * Terminate the string.
191*7c478bd9Sstevel@tonic-gate  */
192*7c478bd9Sstevel@tonic-gate     path->name[pathlen] = '\0';
193*7c478bd9Sstevel@tonic-gate   } else {
194*7c478bd9Sstevel@tonic-gate /*
195*7c478bd9Sstevel@tonic-gate  * Append the string directly to the pathname.
196*7c478bd9Sstevel@tonic-gate  */
197*7c478bd9Sstevel@tonic-gate     memcpy(path->name + pathlen, string, slen);
198*7c478bd9Sstevel@tonic-gate     path->name[pathlen + slen] = '\0';
199*7c478bd9Sstevel@tonic-gate   };
200*7c478bd9Sstevel@tonic-gate   return path->name;
201*7c478bd9Sstevel@tonic-gate }
202*7c478bd9Sstevel@tonic-gate 
203*7c478bd9Sstevel@tonic-gate /*.......................................................................
204*7c478bd9Sstevel@tonic-gate  * Prepend a string to a pathname, increasing the size of the pathname
205*7c478bd9Sstevel@tonic-gate  * buffer if needed.
206*7c478bd9Sstevel@tonic-gate  *
207*7c478bd9Sstevel@tonic-gate  * Input:
208*7c478bd9Sstevel@tonic-gate  *  path        PathName *  The pathname container.
209*7c478bd9Sstevel@tonic-gate  *  string    const char *  The string to be prepended to the pathname.
210*7c478bd9Sstevel@tonic-gate  *                          Note that regardless of the slen argument,
211*7c478bd9Sstevel@tonic-gate  *                          this should be a '\0' terminated string.
212*7c478bd9Sstevel@tonic-gate  *  slen             int    The maximum number of characters to prepend
213*7c478bd9Sstevel@tonic-gate  *                          from string[], or -1 to append the whole
214*7c478bd9Sstevel@tonic-gate  *                          string.
215*7c478bd9Sstevel@tonic-gate  *  remove_escapes   int    If true, remove the backslashes that escape
216*7c478bd9Sstevel@tonic-gate  *                          spaces, tabs, backslashes etc..
217*7c478bd9Sstevel@tonic-gate  * Output:
218*7c478bd9Sstevel@tonic-gate  *  return          char *  The pathname string path->name[], which may
219*7c478bd9Sstevel@tonic-gate  *                          have been reallocated, or NULL if there was
220*7c478bd9Sstevel@tonic-gate  *                          insufficient memory to extend the pathname.
221*7c478bd9Sstevel@tonic-gate  */
222*7c478bd9Sstevel@tonic-gate char *_pn_prepend_to_path(PathName *path, const char *string, int slen,
223*7c478bd9Sstevel@tonic-gate 			  int remove_escapes)
224*7c478bd9Sstevel@tonic-gate {
225*7c478bd9Sstevel@tonic-gate   int pathlen;     /* The length of the pathname */
226*7c478bd9Sstevel@tonic-gate   int shift;       /* The number of characters to shift the suffix by */
227*7c478bd9Sstevel@tonic-gate   int i,j;
228*7c478bd9Sstevel@tonic-gate /*
229*7c478bd9Sstevel@tonic-gate  * Check the arguments.
230*7c478bd9Sstevel@tonic-gate  */
231*7c478bd9Sstevel@tonic-gate   if(!path || !string) {
232*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
233*7c478bd9Sstevel@tonic-gate     return NULL;
234*7c478bd9Sstevel@tonic-gate   };
235*7c478bd9Sstevel@tonic-gate /*
236*7c478bd9Sstevel@tonic-gate  * Get the current length of the pathname.
237*7c478bd9Sstevel@tonic-gate  */
238*7c478bd9Sstevel@tonic-gate   pathlen = strlen(path->name);
239*7c478bd9Sstevel@tonic-gate /*
240*7c478bd9Sstevel@tonic-gate  * How many characters should be appended?
241*7c478bd9Sstevel@tonic-gate  */
242*7c478bd9Sstevel@tonic-gate   if(slen < 0 || slen > strlen(string))
243*7c478bd9Sstevel@tonic-gate     slen = strlen(string);
244*7c478bd9Sstevel@tonic-gate /*
245*7c478bd9Sstevel@tonic-gate  * Work out how far we need to shift the original path string to make
246*7c478bd9Sstevel@tonic-gate  * way for the new prefix. When removing escape characters, we need
247*7c478bd9Sstevel@tonic-gate  * final length of the new prefix, after unescaped backslashes have
248*7c478bd9Sstevel@tonic-gate  * been removed.
249*7c478bd9Sstevel@tonic-gate  */
250*7c478bd9Sstevel@tonic-gate   if(remove_escapes) {
251*7c478bd9Sstevel@tonic-gate     int is_escape = 0;
252*7c478bd9Sstevel@tonic-gate     for(shift=0,i=0; i<slen; i++) {
253*7c478bd9Sstevel@tonic-gate       is_escape = !is_escape && string[i] == '\\';
254*7c478bd9Sstevel@tonic-gate       if(!is_escape)
255*7c478bd9Sstevel@tonic-gate 	shift++;
256*7c478bd9Sstevel@tonic-gate     };
257*7c478bd9Sstevel@tonic-gate   } else {
258*7c478bd9Sstevel@tonic-gate     shift = slen;
259*7c478bd9Sstevel@tonic-gate   };
260*7c478bd9Sstevel@tonic-gate /*
261*7c478bd9Sstevel@tonic-gate  * Resize the pathname if needed.
262*7c478bd9Sstevel@tonic-gate  */
263*7c478bd9Sstevel@tonic-gate   if(!_pn_resize_path(path, pathlen + shift))
264*7c478bd9Sstevel@tonic-gate     return NULL;
265*7c478bd9Sstevel@tonic-gate /*
266*7c478bd9Sstevel@tonic-gate  * Make room for the prefix at the beginning of the string.
267*7c478bd9Sstevel@tonic-gate  */
268*7c478bd9Sstevel@tonic-gate   memmove(path->name + shift, path->name, pathlen+1);
269*7c478bd9Sstevel@tonic-gate /*
270*7c478bd9Sstevel@tonic-gate  * Copy the new prefix into the vacated space at the beginning of the
271*7c478bd9Sstevel@tonic-gate  * output pathname, removing any escape characters if needed.
272*7c478bd9Sstevel@tonic-gate  */
273*7c478bd9Sstevel@tonic-gate   if(remove_escapes) {
274*7c478bd9Sstevel@tonic-gate     int is_escape = 0;
275*7c478bd9Sstevel@tonic-gate     for(i=j=0; i<slen; i++) {
276*7c478bd9Sstevel@tonic-gate       is_escape = !is_escape && string[i] == '\\';
277*7c478bd9Sstevel@tonic-gate       if(!is_escape)
278*7c478bd9Sstevel@tonic-gate 	path->name[j++] = string[i];
279*7c478bd9Sstevel@tonic-gate     };
280*7c478bd9Sstevel@tonic-gate   } else {
281*7c478bd9Sstevel@tonic-gate     memcpy(path->name, string, slen);
282*7c478bd9Sstevel@tonic-gate   };
283*7c478bd9Sstevel@tonic-gate   return path->name;
284*7c478bd9Sstevel@tonic-gate }
285*7c478bd9Sstevel@tonic-gate 
286*7c478bd9Sstevel@tonic-gate /*.......................................................................
287*7c478bd9Sstevel@tonic-gate  * If needed reallocate a given pathname buffer to allow a string of
288*7c478bd9Sstevel@tonic-gate  * a given length to be stored in it.
289*7c478bd9Sstevel@tonic-gate  *
290*7c478bd9Sstevel@tonic-gate  * Input:
291*7c478bd9Sstevel@tonic-gate  *  path     PathName *  The pathname container object.
292*7c478bd9Sstevel@tonic-gate  *  length     size_t    The required length of the pathname buffer,
293*7c478bd9Sstevel@tonic-gate  *                       not including the terminating '\0'.
294*7c478bd9Sstevel@tonic-gate  * Output:
295*7c478bd9Sstevel@tonic-gate  *  return       char *  The pathname buffer, or NULL if there was
296*7c478bd9Sstevel@tonic-gate  *                       insufficient memory.
297*7c478bd9Sstevel@tonic-gate  */
298*7c478bd9Sstevel@tonic-gate char *_pn_resize_path(PathName *path, size_t length)
299*7c478bd9Sstevel@tonic-gate {
300*7c478bd9Sstevel@tonic-gate /*
301*7c478bd9Sstevel@tonic-gate  * Check the arguments.
302*7c478bd9Sstevel@tonic-gate  */
303*7c478bd9Sstevel@tonic-gate   if(!path) {
304*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
305*7c478bd9Sstevel@tonic-gate     return NULL;
306*7c478bd9Sstevel@tonic-gate   };
307*7c478bd9Sstevel@tonic-gate /*
308*7c478bd9Sstevel@tonic-gate  * If the pathname buffer isn't large enough to accomodate a string
309*7c478bd9Sstevel@tonic-gate  * of the specified length, attempt to reallocate it with the new
310*7c478bd9Sstevel@tonic-gate  * size, plus space for a terminating '\0'. Also add a bit of
311*7c478bd9Sstevel@tonic-gate  * head room to prevent too many reallocations if the initial length
312*7c478bd9Sstevel@tonic-gate  * turned out to be very optimistic.
313*7c478bd9Sstevel@tonic-gate  */
314*7c478bd9Sstevel@tonic-gate   if(length + 1 > path->dim) {
315*7c478bd9Sstevel@tonic-gate     size_t dim =  length + 1 + PN_PATHNAME_INC;
316*7c478bd9Sstevel@tonic-gate     char *name = (char *) realloc(path->name, dim);
317*7c478bd9Sstevel@tonic-gate     if(!name)
318*7c478bd9Sstevel@tonic-gate       return NULL;
319*7c478bd9Sstevel@tonic-gate     path->name = name;
320*7c478bd9Sstevel@tonic-gate     path->dim = dim;
321*7c478bd9Sstevel@tonic-gate   };
322*7c478bd9Sstevel@tonic-gate   return path->name;
323*7c478bd9Sstevel@tonic-gate }
324*7c478bd9Sstevel@tonic-gate 
325*7c478bd9Sstevel@tonic-gate /*.......................................................................
326*7c478bd9Sstevel@tonic-gate  * Estimate the largest amount of space needed to store a pathname.
327*7c478bd9Sstevel@tonic-gate  *
328*7c478bd9Sstevel@tonic-gate  * Output:
329*7c478bd9Sstevel@tonic-gate  *  return size_t   The number of bytes needed, including space for the
330*7c478bd9Sstevel@tonic-gate  *                  terminating '\0'.
331*7c478bd9Sstevel@tonic-gate  */
332*7c478bd9Sstevel@tonic-gate size_t _pu_pathname_dim(void)
333*7c478bd9Sstevel@tonic-gate {
334*7c478bd9Sstevel@tonic-gate   int maxlen;   /* The return value excluding space for the '\0' */
335*7c478bd9Sstevel@tonic-gate /*
336*7c478bd9Sstevel@tonic-gate  * If the POSIX PATH_MAX macro is defined in limits.h, use it.
337*7c478bd9Sstevel@tonic-gate  */
338*7c478bd9Sstevel@tonic-gate #ifdef PATH_MAX
339*7c478bd9Sstevel@tonic-gate   maxlen = PATH_MAX;
340*7c478bd9Sstevel@tonic-gate /*
341*7c478bd9Sstevel@tonic-gate  * If we have pathconf, use it.
342*7c478bd9Sstevel@tonic-gate  */
343*7c478bd9Sstevel@tonic-gate #elif defined(_PC_PATH_MAX)
344*7c478bd9Sstevel@tonic-gate   errno = 0;
345*7c478bd9Sstevel@tonic-gate   maxlen = pathconf(FS_ROOT_DIR, _PC_PATH_MAX);
346*7c478bd9Sstevel@tonic-gate   if(maxlen <= 0 || errno)
347*7c478bd9Sstevel@tonic-gate     maxlen = MAX_PATHLEN_FALLBACK;
348*7c478bd9Sstevel@tonic-gate /*
349*7c478bd9Sstevel@tonic-gate  * None of the above approaches worked, so substitute our fallback
350*7c478bd9Sstevel@tonic-gate  * guess.
351*7c478bd9Sstevel@tonic-gate  */
352*7c478bd9Sstevel@tonic-gate #else
353*7c478bd9Sstevel@tonic-gate     maxlen = MAX_PATHLEN_FALLBACK;
354*7c478bd9Sstevel@tonic-gate #endif
355*7c478bd9Sstevel@tonic-gate /*
356*7c478bd9Sstevel@tonic-gate  * Return the amount of space needed to accomodate a pathname plus
357*7c478bd9Sstevel@tonic-gate  * a terminating '\0'.
358*7c478bd9Sstevel@tonic-gate  */
359*7c478bd9Sstevel@tonic-gate   return maxlen + 1;
360*7c478bd9Sstevel@tonic-gate }
361*7c478bd9Sstevel@tonic-gate 
362*7c478bd9Sstevel@tonic-gate /*.......................................................................
363*7c478bd9Sstevel@tonic-gate  * Return non-zero if the specified path name refers to a directory.
364*7c478bd9Sstevel@tonic-gate  *
365*7c478bd9Sstevel@tonic-gate  * Input:
366*7c478bd9Sstevel@tonic-gate  *  pathname  const char *  The path to test.
367*7c478bd9Sstevel@tonic-gate  * Output:
368*7c478bd9Sstevel@tonic-gate  *  return           int    0 - Not a directory.
369*7c478bd9Sstevel@tonic-gate  *                          1 - pathname[] refers to a directory.
370*7c478bd9Sstevel@tonic-gate  */
371*7c478bd9Sstevel@tonic-gate int _pu_path_is_dir(const char *pathname)
372*7c478bd9Sstevel@tonic-gate {
373*7c478bd9Sstevel@tonic-gate   struct stat statbuf;    /* The file-statistics return buffer */
374*7c478bd9Sstevel@tonic-gate /*
375*7c478bd9Sstevel@tonic-gate  * Look up the file attributes.
376*7c478bd9Sstevel@tonic-gate  */
377*7c478bd9Sstevel@tonic-gate   if(stat(pathname, &statbuf) < 0)
378*7c478bd9Sstevel@tonic-gate     return 0;
379*7c478bd9Sstevel@tonic-gate /*
380*7c478bd9Sstevel@tonic-gate  * Is the file a directory?
381*7c478bd9Sstevel@tonic-gate  */
382*7c478bd9Sstevel@tonic-gate   return S_ISDIR(statbuf.st_mode) != 0;
383*7c478bd9Sstevel@tonic-gate }
384*7c478bd9Sstevel@tonic-gate 
385*7c478bd9Sstevel@tonic-gate /*.......................................................................
386*7c478bd9Sstevel@tonic-gate  * Return non-zero if the specified path name refers to a regular file.
387*7c478bd9Sstevel@tonic-gate  *
388*7c478bd9Sstevel@tonic-gate  * Input:
389*7c478bd9Sstevel@tonic-gate  *  pathname  const char *  The path to test.
390*7c478bd9Sstevel@tonic-gate  * Output:
391*7c478bd9Sstevel@tonic-gate  *  return           int    0 - Not a regular file.
392*7c478bd9Sstevel@tonic-gate  *                          1 - pathname[] refers to a regular file.
393*7c478bd9Sstevel@tonic-gate  */
394*7c478bd9Sstevel@tonic-gate int _pu_path_is_file(const char *pathname)
395*7c478bd9Sstevel@tonic-gate {
396*7c478bd9Sstevel@tonic-gate   struct stat statbuf;    /* The file-statistics return buffer */
397*7c478bd9Sstevel@tonic-gate /*
398*7c478bd9Sstevel@tonic-gate  * Look up the file attributes.
399*7c478bd9Sstevel@tonic-gate  */
400*7c478bd9Sstevel@tonic-gate   if(stat(pathname, &statbuf) < 0)
401*7c478bd9Sstevel@tonic-gate     return 0;
402*7c478bd9Sstevel@tonic-gate /*
403*7c478bd9Sstevel@tonic-gate  * Is the file a regular file?
404*7c478bd9Sstevel@tonic-gate  */
405*7c478bd9Sstevel@tonic-gate   return S_ISREG(statbuf.st_mode) != 0;
406*7c478bd9Sstevel@tonic-gate }
407*7c478bd9Sstevel@tonic-gate 
408*7c478bd9Sstevel@tonic-gate /*.......................................................................
409*7c478bd9Sstevel@tonic-gate  * Return non-zero if the specified path name refers to an executable.
410*7c478bd9Sstevel@tonic-gate  *
411*7c478bd9Sstevel@tonic-gate  * Input:
412*7c478bd9Sstevel@tonic-gate  *  pathname  const char *  The path to test.
413*7c478bd9Sstevel@tonic-gate  * Output:
414*7c478bd9Sstevel@tonic-gate  *  return           int    0 - Not an executable file.
415*7c478bd9Sstevel@tonic-gate  *                          1 - pathname[] refers to an executable file.
416*7c478bd9Sstevel@tonic-gate  */
417*7c478bd9Sstevel@tonic-gate int _pu_path_is_exe(const char *pathname)
418*7c478bd9Sstevel@tonic-gate {
419*7c478bd9Sstevel@tonic-gate   struct stat statbuf;    /* The file-statistics return buffer */
420*7c478bd9Sstevel@tonic-gate /*
421*7c478bd9Sstevel@tonic-gate  * Look up the file attributes.
422*7c478bd9Sstevel@tonic-gate  */
423*7c478bd9Sstevel@tonic-gate   if(stat(pathname, &statbuf) < 0)
424*7c478bd9Sstevel@tonic-gate     return 0;
425*7c478bd9Sstevel@tonic-gate /*
426*7c478bd9Sstevel@tonic-gate  * Is the file a regular file which is executable by the current user.
427*7c478bd9Sstevel@tonic-gate  */
428*7c478bd9Sstevel@tonic-gate   return S_ISREG(statbuf.st_mode) != 0 &&
429*7c478bd9Sstevel@tonic-gate     (statbuf.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)) &&
430*7c478bd9Sstevel@tonic-gate     access(pathname, X_OK) == 0;
431*7c478bd9Sstevel@tonic-gate }
432*7c478bd9Sstevel@tonic-gate 
433*7c478bd9Sstevel@tonic-gate /*.......................................................................
434*7c478bd9Sstevel@tonic-gate  * Search backwards for the potential start of a filename. This
435*7c478bd9Sstevel@tonic-gate  * looks backwards from the specified index in a given string,
436*7c478bd9Sstevel@tonic-gate  * stopping at the first unescaped space or the start of the line.
437*7c478bd9Sstevel@tonic-gate  *
438*7c478bd9Sstevel@tonic-gate  * Input:
439*7c478bd9Sstevel@tonic-gate  *  string  const char *  The string to search backwards in.
440*7c478bd9Sstevel@tonic-gate  *  back_from      int    The index of the first character in string[]
441*7c478bd9Sstevel@tonic-gate  *                        that follows the pathname.
442*7c478bd9Sstevel@tonic-gate  * Output:
443*7c478bd9Sstevel@tonic-gate  *  return        char *  The pointer to the first character of
444*7c478bd9Sstevel@tonic-gate  *                        the potential pathname, or NULL on error.
445*7c478bd9Sstevel@tonic-gate  */
446*7c478bd9Sstevel@tonic-gate char *_pu_start_of_path(const char *string, int back_from)
447*7c478bd9Sstevel@tonic-gate {
448*7c478bd9Sstevel@tonic-gate   int i, j;
449*7c478bd9Sstevel@tonic-gate /*
450*7c478bd9Sstevel@tonic-gate  * Check the arguments.
451*7c478bd9Sstevel@tonic-gate  */
452*7c478bd9Sstevel@tonic-gate   if(!string || back_from < 0) {
453*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
454*7c478bd9Sstevel@tonic-gate     return NULL;
455*7c478bd9Sstevel@tonic-gate   };
456*7c478bd9Sstevel@tonic-gate /*
457*7c478bd9Sstevel@tonic-gate  * Search backwards from the specified index.
458*7c478bd9Sstevel@tonic-gate  */
459*7c478bd9Sstevel@tonic-gate   for(i=back_from-1; i>=0; i--) {
460*7c478bd9Sstevel@tonic-gate     int c = string[i];
461*7c478bd9Sstevel@tonic-gate /*
462*7c478bd9Sstevel@tonic-gate  * Stop on unescaped spaces.
463*7c478bd9Sstevel@tonic-gate  */
464*7c478bd9Sstevel@tonic-gate     if(isspace((int)(unsigned char)c)) {
465*7c478bd9Sstevel@tonic-gate /*
466*7c478bd9Sstevel@tonic-gate  * The space can't be escaped if we are at the start of the line.
467*7c478bd9Sstevel@tonic-gate  */
468*7c478bd9Sstevel@tonic-gate       if(i==0)
469*7c478bd9Sstevel@tonic-gate         break;
470*7c478bd9Sstevel@tonic-gate /*
471*7c478bd9Sstevel@tonic-gate  * Find the extent of the escape characters which precedes the space.
472*7c478bd9Sstevel@tonic-gate  */
473*7c478bd9Sstevel@tonic-gate       for(j=i-1; j>=0 && string[j]=='\\'; j--)
474*7c478bd9Sstevel@tonic-gate 	;
475*7c478bd9Sstevel@tonic-gate /*
476*7c478bd9Sstevel@tonic-gate  * If there isn't an odd number of escape characters before the space,
477*7c478bd9Sstevel@tonic-gate  * then the space isn't escaped.
478*7c478bd9Sstevel@tonic-gate  */
479*7c478bd9Sstevel@tonic-gate       if((i - 1 - j) % 2 == 0)
480*7c478bd9Sstevel@tonic-gate 	break;
481*7c478bd9Sstevel@tonic-gate     };
482*7c478bd9Sstevel@tonic-gate   };
483*7c478bd9Sstevel@tonic-gate   return (char *)string + i + 1;
484*7c478bd9Sstevel@tonic-gate }
485*7c478bd9Sstevel@tonic-gate 
486*7c478bd9Sstevel@tonic-gate /*.......................................................................
487*7c478bd9Sstevel@tonic-gate  * Find the length of a potential filename starting from a given
488*7c478bd9Sstevel@tonic-gate  * point. This looks forwards from the specified index in a given string,
489*7c478bd9Sstevel@tonic-gate  * stopping at the first unescaped space or the end of the line.
490*7c478bd9Sstevel@tonic-gate  *
491*7c478bd9Sstevel@tonic-gate  * Input:
492*7c478bd9Sstevel@tonic-gate  *  string   const char *  The string to search backwards in.
493*7c478bd9Sstevel@tonic-gate  *  start_from      int    The index of the first character of the pathname
494*7c478bd9Sstevel@tonic-gate  *                         in string[].
495*7c478bd9Sstevel@tonic-gate  * Output:
496*7c478bd9Sstevel@tonic-gate  *  return         char *  The pointer to the character that follows
497*7c478bd9Sstevel@tonic-gate  *                         the potential pathname, or NULL on error.
498*7c478bd9Sstevel@tonic-gate  */
499*7c478bd9Sstevel@tonic-gate char *_pu_end_of_path(const char *string, int start_from)
500*7c478bd9Sstevel@tonic-gate {
501*7c478bd9Sstevel@tonic-gate   int c;             /* The character being examined */
502*7c478bd9Sstevel@tonic-gate   int escaped = 0;   /* True when the next character is escaped */
503*7c478bd9Sstevel@tonic-gate   int i;
504*7c478bd9Sstevel@tonic-gate /*
505*7c478bd9Sstevel@tonic-gate  * Check the arguments.
506*7c478bd9Sstevel@tonic-gate  */
507*7c478bd9Sstevel@tonic-gate   if(!string || start_from < 0) {
508*7c478bd9Sstevel@tonic-gate     errno = EINVAL;
509*7c478bd9Sstevel@tonic-gate     return NULL;
510*7c478bd9Sstevel@tonic-gate   };
511*7c478bd9Sstevel@tonic-gate /*
512*7c478bd9Sstevel@tonic-gate  * Search forwards from the specified index.
513*7c478bd9Sstevel@tonic-gate  */
514*7c478bd9Sstevel@tonic-gate   for(i=start_from; (c=string[i]) != '\0'; i++) {
515*7c478bd9Sstevel@tonic-gate     if(escaped) {
516*7c478bd9Sstevel@tonic-gate       escaped = 0;
517*7c478bd9Sstevel@tonic-gate     } else if(isspace(c)) {
518*7c478bd9Sstevel@tonic-gate       break;
519*7c478bd9Sstevel@tonic-gate     } else if(c == '\\') {
520*7c478bd9Sstevel@tonic-gate       escaped = 1;
521*7c478bd9Sstevel@tonic-gate     };
522*7c478bd9Sstevel@tonic-gate   };
523*7c478bd9Sstevel@tonic-gate   return (char *)string + i;
524*7c478bd9Sstevel@tonic-gate }
525*7c478bd9Sstevel@tonic-gate 
526*7c478bd9Sstevel@tonic-gate /*.......................................................................
527*7c478bd9Sstevel@tonic-gate  * Return non-zero if the specified path name refers to an existing file.
528*7c478bd9Sstevel@tonic-gate  *
529*7c478bd9Sstevel@tonic-gate  * Input:
530*7c478bd9Sstevel@tonic-gate  *  pathname   const char *  The path to test.
531*7c478bd9Sstevel@tonic-gate  * Output:
532*7c478bd9Sstevel@tonic-gate  *  return            int    0 - The file doesn't exist.
533*7c478bd9Sstevel@tonic-gate  *                           1 - The file does exist.
534*7c478bd9Sstevel@tonic-gate  */
535*7c478bd9Sstevel@tonic-gate int _pu_file_exists(const char *pathname)
536*7c478bd9Sstevel@tonic-gate {
537*7c478bd9Sstevel@tonic-gate   struct stat statbuf;
538*7c478bd9Sstevel@tonic-gate   return stat(pathname, &statbuf) == 0;
539*7c478bd9Sstevel@tonic-gate }
540*7c478bd9Sstevel@tonic-gate 
541*7c478bd9Sstevel@tonic-gate #endif  /* ifndef WITHOUT_FILE_SYSTEM */
542