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.
33 * If file-system access is to be excluded, this module has no function,
34 * so all of its code should be excluded.
36 #ifndef WITHOUT_FILE_SYSTEM
46 #include <sys/types.h>
51 /*.......................................................................
52 * Create a new PathName object.
55 * return PathName * The new object, or NULL on error.
57 PathName
*_new_PathName(void)
59 PathName
*path
; /* The object to be returned */
61 * Allocate the container.
63 path
= (PathName
*) malloc(sizeof(PathName
));
69 * Before attempting any operation that might fail, initialize the
70 * container at least up to the point at which it can safely be passed
76 * Figure out the maximum length of an expanded pathname.
78 path
->dim
= _pu_pathname_dim();
80 return _del_PathName(path
);
82 * Allocate the pathname buffer.
84 path
->name
= (char *)malloc(path
->dim
* sizeof(char));
87 return _del_PathName(path
);
92 /*.......................................................................
93 * Delete a PathName object.
96 * path PathName * The object to be deleted.
98 * return PathName * The deleted object (always NULL).
100 PathName
*_del_PathName(PathName
*path
)
109 /*.......................................................................
110 * Return the pathname to a zero-length string.
113 * path PathName * The pathname container.
115 * return char * The cleared pathname buffer, or NULL on error.
117 char *_pn_clear_path(PathName
*path
)
120 * Check the arguments.
126 path
->name
[0] = '\0';
130 /*.......................................................................
131 * Append a string to a pathname, increasing the size of the pathname
135 * path PathName * The pathname container.
136 * string const char * The string to be appended to the pathname.
137 * Note that regardless of the slen argument,
138 * this should be a '\0' terminated string.
139 * slen int The maximum number of characters to append
140 * from string[], or -1 to append the whole
142 * remove_escapes int If true, remove the backslashes that escape
143 * spaces, tabs, backslashes etc..
145 * return char * The pathname string path->name[], which may
146 * have been reallocated, or NULL if there was
147 * insufficient memory to extend the pathname.
149 char *_pn_append_to_path(PathName
*path
, const char *string
, int slen
,
152 int pathlen
; /* The length of the pathname */
155 * Check the arguments.
157 if(!path
|| !string
) {
162 * Get the current length of the pathname.
164 pathlen
= strlen(path
->name
);
166 * How many characters should be appended?
168 if(slen
< 0 || slen
> strlen(string
))
169 slen
= strlen(string
);
171 * Resize the pathname if needed.
173 if(!_pn_resize_path(path
, pathlen
+ slen
))
176 * Append the string to the output pathname, removing any escape
177 * characters found therein.
181 for(i
=0; i
<slen
; i
++) {
182 is_escape
= !is_escape
&& string
[i
] == '\\';
184 path
->name
[pathlen
++] = string
[i
];
187 * Terminate the string.
189 path
->name
[pathlen
] = '\0';
192 * Append the string directly to the pathname.
194 memcpy(path
->name
+ pathlen
, string
, slen
);
195 path
->name
[pathlen
+ slen
] = '\0';
200 /*.......................................................................
201 * Prepend a string to a pathname, increasing the size of the pathname
205 * path PathName * The pathname container.
206 * string const char * The string to be prepended to the pathname.
207 * Note that regardless of the slen argument,
208 * this should be a '\0' terminated string.
209 * slen int The maximum number of characters to prepend
210 * from string[], or -1 to append the whole
212 * remove_escapes int If true, remove the backslashes that escape
213 * spaces, tabs, backslashes etc..
215 * return char * The pathname string path->name[], which may
216 * have been reallocated, or NULL if there was
217 * insufficient memory to extend the pathname.
219 char *_pn_prepend_to_path(PathName
*path
, const char *string
, int slen
,
222 int pathlen
; /* The length of the pathname */
223 int shift
; /* The number of characters to shift the suffix by */
226 * Check the arguments.
228 if(!path
|| !string
) {
233 * Get the current length of the pathname.
235 pathlen
= strlen(path
->name
);
237 * How many characters should be appended?
239 if(slen
< 0 || slen
> strlen(string
))
240 slen
= strlen(string
);
242 * Work out how far we need to shift the original path string to make
243 * way for the new prefix. When removing escape characters, we need
244 * final length of the new prefix, after unescaped backslashes have
249 for(shift
=0,i
=0; i
<slen
; i
++) {
250 is_escape
= !is_escape
&& string
[i
] == '\\';
258 * Resize the pathname if needed.
260 if(!_pn_resize_path(path
, pathlen
+ shift
))
263 * Make room for the prefix at the beginning of the string.
265 memmove(path
->name
+ shift
, path
->name
, pathlen
+1);
267 * Copy the new prefix into the vacated space at the beginning of the
268 * output pathname, removing any escape characters if needed.
272 for(i
=j
=0; i
<slen
; i
++) {
273 is_escape
= !is_escape
&& string
[i
] == '\\';
275 path
->name
[j
++] = string
[i
];
278 memcpy(path
->name
, string
, slen
);
283 /*.......................................................................
284 * If needed reallocate a given pathname buffer to allow a string of
285 * a given length to be stored in it.
288 * path PathName * The pathname container object.
289 * length size_t The required length of the pathname buffer,
290 * not including the terminating '\0'.
292 * return char * The pathname buffer, or NULL if there was
293 * insufficient memory.
295 char *_pn_resize_path(PathName
*path
, size_t length
)
298 * Check the arguments.
305 * If the pathname buffer isn't large enough to accomodate a string
306 * of the specified length, attempt to reallocate it with the new
307 * size, plus space for a terminating '\0'. Also add a bit of
308 * head room to prevent too many reallocations if the initial length
309 * turned out to be very optimistic.
311 if(length
+ 1 > path
->dim
) {
312 size_t dim
= length
+ 1 + PN_PATHNAME_INC
;
313 char *name
= (char *) realloc(path
->name
, dim
);
322 /*.......................................................................
323 * Estimate the largest amount of space needed to store a pathname.
326 * return size_t The number of bytes needed, including space for the
329 size_t _pu_pathname_dim(void)
331 int maxlen
; /* The return value excluding space for the '\0' */
333 * If the POSIX PATH_MAX macro is defined in limits.h, use it.
338 * If we have pathconf, use it.
340 #elif defined(_PC_PATH_MAX)
342 maxlen
= pathconf(FS_ROOT_DIR
, _PC_PATH_MAX
);
343 if(maxlen
<= 0 || errno
)
344 maxlen
= MAX_PATHLEN_FALLBACK
;
346 * None of the above approaches worked, so substitute our fallback
350 maxlen
= MAX_PATHLEN_FALLBACK
;
353 * Return the amount of space needed to accomodate a pathname plus
354 * a terminating '\0'.
359 /*.......................................................................
360 * Return non-zero if the specified path name refers to a directory.
363 * pathname const char * The path to test.
365 * return int 0 - Not a directory.
366 * 1 - pathname[] refers to a directory.
368 int _pu_path_is_dir(const char *pathname
)
370 struct stat statbuf
; /* The file-statistics return buffer */
372 * Look up the file attributes.
374 if(stat(pathname
, &statbuf
) < 0)
377 * Is the file a directory?
379 return S_ISDIR(statbuf
.st_mode
) != 0;
382 /*.......................................................................
383 * Return non-zero if the specified path name refers to a regular file.
386 * pathname const char * The path to test.
388 * return int 0 - Not a regular file.
389 * 1 - pathname[] refers to a regular file.
391 int _pu_path_is_file(const char *pathname
)
393 struct stat statbuf
; /* The file-statistics return buffer */
395 * Look up the file attributes.
397 if(stat(pathname
, &statbuf
) < 0)
400 * Is the file a regular file?
402 return S_ISREG(statbuf
.st_mode
) != 0;
405 /*.......................................................................
406 * Return non-zero if the specified path name refers to an executable.
409 * pathname const char * The path to test.
411 * return int 0 - Not an executable file.
412 * 1 - pathname[] refers to an executable file.
414 int _pu_path_is_exe(const char *pathname
)
416 struct stat statbuf
; /* The file-statistics return buffer */
418 * Look up the file attributes.
420 if(stat(pathname
, &statbuf
) < 0)
423 * Is the file a regular file which is executable by the current user.
425 return S_ISREG(statbuf
.st_mode
) != 0 &&
426 (statbuf
.st_mode
& (S_IXOTH
| S_IXGRP
| S_IXUSR
)) &&
427 access(pathname
, X_OK
) == 0;
430 /*.......................................................................
431 * Search backwards for the potential start of a filename. This
432 * looks backwards from the specified index in a given string,
433 * stopping at the first unescaped space or the start of the line.
436 * string const char * The string to search backwards in.
437 * back_from int The index of the first character in string[]
438 * that follows the pathname.
440 * return char * The pointer to the first character of
441 * the potential pathname, or NULL on error.
443 char *_pu_start_of_path(const char *string
, int back_from
)
447 * Check the arguments.
449 if(!string
|| back_from
< 0) {
454 * Search backwards from the specified index.
456 for(i
=back_from
-1; i
>=0; i
--) {
459 * Stop on unescaped spaces.
461 if(isspace((int)(unsigned char)c
)) {
463 * The space can't be escaped if we are at the start of the line.
468 * Find the extent of the escape characters which precedes the space.
470 for(j
=i
-1; j
>=0 && string
[j
]=='\\'; j
--)
473 * If there isn't an odd number of escape characters before the space,
474 * then the space isn't escaped.
476 if((i
- 1 - j
) % 2 == 0)
480 return (char *)string
+ i
+ 1;
483 /*.......................................................................
484 * Find the length of a potential filename starting from a given
485 * point. This looks forwards from the specified index in a given string,
486 * stopping at the first unescaped space or the end of the line.
489 * string const char * The string to search backwards in.
490 * start_from int The index of the first character of the pathname
493 * return char * The pointer to the character that follows
494 * the potential pathname, or NULL on error.
496 char *_pu_end_of_path(const char *string
, int start_from
)
498 int c
; /* The character being examined */
499 int escaped
= 0; /* True when the next character is escaped */
502 * Check the arguments.
504 if(!string
|| start_from
< 0) {
509 * Search forwards from the specified index.
511 for(i
=start_from
; (c
=string
[i
]) != '\0'; i
++) {
514 } else if(isspace(c
)) {
516 } else if(c
== '\\') {
520 return (char *)string
+ i
;
523 /*.......................................................................
524 * Return non-zero if the specified path name refers to an existing file.
527 * pathname const char * The path to test.
529 * return int 0 - The file doesn't exist.
530 * 1 - The file does exist.
532 int _pu_file_exists(const char *pathname
)
535 return stat(pathname
, &statbuf
) == 0;
538 #endif /* ifndef WITHOUT_FILE_SYSTEM */