2 * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd.
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, and/or sell copies of the Software, and to permit persons
11 * to whom the Software is furnished to do so, provided that the above
12 * copyright notice(s) and this permission notice appear in all copies of
13 * the Software and that both the above copyright notice(s) and this
14 * permission notice appear in supporting documentation.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
19 * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
20 * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
21 * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
22 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
23 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
24 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
26 * Except as contained in this notice, the name of a copyright holder
27 * shall not be used in advertising or otherwise to promote the sale, use
28 * or other dealings in this Software without prior written authorization
29 * of the copyright holder.
32 #pragma ident "%Z%%M% %I% %E% SMI"
35 * If file-system access is to be excluded, this module has no function,
36 * so all of its code should be excluded.
38 #ifndef WITHOUT_FILE_SYSTEM
48 #include <sys/types.h>
53 /*.......................................................................
54 * Create a new PathName object.
57 * return PathName * The new object, or NULL on error.
59 PathName
*_new_PathName(void)
61 PathName
*path
; /* The object to be returned */
63 * Allocate the container.
65 path
= (PathName
*) malloc(sizeof(PathName
));
71 * Before attempting any operation that might fail, initialize the
72 * container at least up to the point at which it can safely be passed
78 * Figure out the maximum length of an expanded pathname.
80 path
->dim
= _pu_pathname_dim();
82 return _del_PathName(path
);
84 * Allocate the pathname buffer.
86 path
->name
= (char *)malloc(path
->dim
* sizeof(char));
89 return _del_PathName(path
);
94 /*.......................................................................
95 * Delete a PathName object.
98 * path PathName * The object to be deleted.
100 * return PathName * The deleted object (always NULL).
102 PathName
*_del_PathName(PathName
*path
)
112 /*.......................................................................
113 * Return the pathname to a zero-length string.
116 * path PathName * The pathname container.
118 * return char * The cleared pathname buffer, or NULL on error.
120 char *_pn_clear_path(PathName
*path
)
123 * Check the arguments.
129 path
->name
[0] = '\0';
133 /*.......................................................................
134 * Append a string to a pathname, increasing the size of the pathname
138 * path PathName * The pathname container.
139 * string const char * The string to be appended to the pathname.
140 * Note that regardless of the slen argument,
141 * this should be a '\0' terminated string.
142 * slen int The maximum number of characters to append
143 * from string[], or -1 to append the whole
145 * remove_escapes int If true, remove the backslashes that escape
146 * spaces, tabs, backslashes etc..
148 * return char * The pathname string path->name[], which may
149 * have been reallocated, or NULL if there was
150 * insufficient memory to extend the pathname.
152 char *_pn_append_to_path(PathName
*path
, const char *string
, int slen
,
155 int pathlen
; /* The length of the pathname */
158 * Check the arguments.
160 if(!path
|| !string
) {
165 * Get the current length of the pathname.
167 pathlen
= strlen(path
->name
);
169 * How many characters should be appended?
171 if(slen
< 0 || slen
> strlen(string
))
172 slen
= strlen(string
);
174 * Resize the pathname if needed.
176 if(!_pn_resize_path(path
, pathlen
+ slen
))
179 * Append the string to the output pathname, removing any escape
180 * characters found therein.
184 for(i
=0; i
<slen
; i
++) {
185 is_escape
= !is_escape
&& string
[i
] == '\\';
187 path
->name
[pathlen
++] = string
[i
];
190 * Terminate the string.
192 path
->name
[pathlen
] = '\0';
195 * Append the string directly to the pathname.
197 memcpy(path
->name
+ pathlen
, string
, slen
);
198 path
->name
[pathlen
+ slen
] = '\0';
203 /*.......................................................................
204 * Prepend a string to a pathname, increasing the size of the pathname
208 * path PathName * The pathname container.
209 * string const char * The string to be prepended to the pathname.
210 * Note that regardless of the slen argument,
211 * this should be a '\0' terminated string.
212 * slen int The maximum number of characters to prepend
213 * from string[], or -1 to append the whole
215 * remove_escapes int If true, remove the backslashes that escape
216 * spaces, tabs, backslashes etc..
218 * return char * The pathname string path->name[], which may
219 * have been reallocated, or NULL if there was
220 * insufficient memory to extend the pathname.
222 char *_pn_prepend_to_path(PathName
*path
, const char *string
, int slen
,
225 int pathlen
; /* The length of the pathname */
226 int shift
; /* The number of characters to shift the suffix by */
229 * Check the arguments.
231 if(!path
|| !string
) {
236 * Get the current length of the pathname.
238 pathlen
= strlen(path
->name
);
240 * How many characters should be appended?
242 if(slen
< 0 || slen
> strlen(string
))
243 slen
= strlen(string
);
245 * Work out how far we need to shift the original path string to make
246 * way for the new prefix. When removing escape characters, we need
247 * final length of the new prefix, after unescaped backslashes have
252 for(shift
=0,i
=0; i
<slen
; i
++) {
253 is_escape
= !is_escape
&& string
[i
] == '\\';
261 * Resize the pathname if needed.
263 if(!_pn_resize_path(path
, pathlen
+ shift
))
266 * Make room for the prefix at the beginning of the string.
268 memmove(path
->name
+ shift
, path
->name
, pathlen
+1);
270 * Copy the new prefix into the vacated space at the beginning of the
271 * output pathname, removing any escape characters if needed.
275 for(i
=j
=0; i
<slen
; i
++) {
276 is_escape
= !is_escape
&& string
[i
] == '\\';
278 path
->name
[j
++] = string
[i
];
281 memcpy(path
->name
, string
, slen
);
286 /*.......................................................................
287 * If needed reallocate a given pathname buffer to allow a string of
288 * a given length to be stored in it.
291 * path PathName * The pathname container object.
292 * length size_t The required length of the pathname buffer,
293 * not including the terminating '\0'.
295 * return char * The pathname buffer, or NULL if there was
296 * insufficient memory.
298 char *_pn_resize_path(PathName
*path
, size_t length
)
301 * Check the arguments.
308 * If the pathname buffer isn't large enough to accomodate a string
309 * of the specified length, attempt to reallocate it with the new
310 * size, plus space for a terminating '\0'. Also add a bit of
311 * head room to prevent too many reallocations if the initial length
312 * turned out to be very optimistic.
314 if(length
+ 1 > path
->dim
) {
315 size_t dim
= length
+ 1 + PN_PATHNAME_INC
;
316 char *name
= (char *) realloc(path
->name
, dim
);
325 /*.......................................................................
326 * Estimate the largest amount of space needed to store a pathname.
329 * return size_t The number of bytes needed, including space for the
332 size_t _pu_pathname_dim(void)
334 int maxlen
; /* The return value excluding space for the '\0' */
336 * If the POSIX PATH_MAX macro is defined in limits.h, use it.
341 * If we have pathconf, use it.
343 #elif defined(_PC_PATH_MAX)
345 maxlen
= pathconf(FS_ROOT_DIR
, _PC_PATH_MAX
);
346 if(maxlen
<= 0 || errno
)
347 maxlen
= MAX_PATHLEN_FALLBACK
;
349 * None of the above approaches worked, so substitute our fallback
353 maxlen
= MAX_PATHLEN_FALLBACK
;
356 * Return the amount of space needed to accomodate a pathname plus
357 * a terminating '\0'.
362 /*.......................................................................
363 * Return non-zero if the specified path name refers to a directory.
366 * pathname const char * The path to test.
368 * return int 0 - Not a directory.
369 * 1 - pathname[] refers to a directory.
371 int _pu_path_is_dir(const char *pathname
)
373 struct stat statbuf
; /* The file-statistics return buffer */
375 * Look up the file attributes.
377 if(stat(pathname
, &statbuf
) < 0)
380 * Is the file a directory?
382 return S_ISDIR(statbuf
.st_mode
) != 0;
385 /*.......................................................................
386 * Return non-zero if the specified path name refers to a regular file.
389 * pathname const char * The path to test.
391 * return int 0 - Not a regular file.
392 * 1 - pathname[] refers to a regular file.
394 int _pu_path_is_file(const char *pathname
)
396 struct stat statbuf
; /* The file-statistics return buffer */
398 * Look up the file attributes.
400 if(stat(pathname
, &statbuf
) < 0)
403 * Is the file a regular file?
405 return S_ISREG(statbuf
.st_mode
) != 0;
408 /*.......................................................................
409 * Return non-zero if the specified path name refers to an executable.
412 * pathname const char * The path to test.
414 * return int 0 - Not an executable file.
415 * 1 - pathname[] refers to an executable file.
417 int _pu_path_is_exe(const char *pathname
)
419 struct stat statbuf
; /* The file-statistics return buffer */
421 * Look up the file attributes.
423 if(stat(pathname
, &statbuf
) < 0)
426 * Is the file a regular file which is executable by the current user.
428 return S_ISREG(statbuf
.st_mode
) != 0 &&
429 (statbuf
.st_mode
& (S_IXOTH
| S_IXGRP
| S_IXUSR
)) &&
430 access(pathname
, X_OK
) == 0;
433 /*.......................................................................
434 * Search backwards for the potential start of a filename. This
435 * looks backwards from the specified index in a given string,
436 * stopping at the first unescaped space or the start of the line.
439 * string const char * The string to search backwards in.
440 * back_from int The index of the first character in string[]
441 * that follows the pathname.
443 * return char * The pointer to the first character of
444 * the potential pathname, or NULL on error.
446 char *_pu_start_of_path(const char *string
, int back_from
)
450 * Check the arguments.
452 if(!string
|| back_from
< 0) {
457 * Search backwards from the specified index.
459 for(i
=back_from
-1; i
>=0; i
--) {
462 * Stop on unescaped spaces.
464 if(isspace((int)(unsigned char)c
)) {
466 * The space can't be escaped if we are at the start of the line.
471 * Find the extent of the escape characters which precedes the space.
473 for(j
=i
-1; j
>=0 && string
[j
]=='\\'; j
--)
476 * If there isn't an odd number of escape characters before the space,
477 * then the space isn't escaped.
479 if((i
- 1 - j
) % 2 == 0)
483 return (char *)string
+ i
+ 1;
486 /*.......................................................................
487 * Find the length of a potential filename starting from a given
488 * point. This looks forwards from the specified index in a given string,
489 * stopping at the first unescaped space or the end of the line.
492 * string const char * The string to search backwards in.
493 * start_from int The index of the first character of the pathname
496 * return char * The pointer to the character that follows
497 * the potential pathname, or NULL on error.
499 char *_pu_end_of_path(const char *string
, int start_from
)
501 int c
; /* The character being examined */
502 int escaped
= 0; /* True when the next character is escaped */
505 * Check the arguments.
507 if(!string
|| start_from
< 0) {
512 * Search forwards from the specified index.
514 for(i
=start_from
; (c
=string
[i
]) != '\0'; i
++) {
517 } else if(isspace(c
)) {
519 } else if(c
== '\\') {
523 return (char *)string
+ i
;
526 /*.......................................................................
527 * Return non-zero if the specified path name refers to an existing file.
530 * pathname const char * The path to test.
532 * return int 0 - The file doesn't exist.
533 * 1 - The file does exist.
535 int _pu_file_exists(const char *pathname
)
538 return stat(pathname
, &statbuf
) == 0;
541 #endif /* ifndef WITHOUT_FILE_SYSTEM */