dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / libtecla / common / pathutil.c
blob25fda360c6ded138d07ccf0e4d8004d63d5e6c56
1 /*
2 * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd.
3 *
4 * All rights reserved.
5 *
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
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <errno.h>
41 #include <string.h>
42 #include <ctype.h>
43 #include <limits.h>
45 #include <unistd.h>
46 #include <sys/types.h>
47 #include <sys/stat.h>
49 #include "pathutil.h"
51 /*.......................................................................
52 * Create a new PathName object.
54 * Output:
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));
64 if(!path) {
65 errno = ENOMEM;
66 return NULL;
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
71 * to _del_PathName().
73 path->name = NULL;
74 path->dim = 0;
76 * Figure out the maximum length of an expanded pathname.
78 path->dim = _pu_pathname_dim();
79 if(path->dim == 0)
80 return _del_PathName(path);
82 * Allocate the pathname buffer.
84 path->name = (char *)malloc(path->dim * sizeof(char));
85 if(!path->name) {
86 errno = ENOMEM;
87 return _del_PathName(path);
89 return path;
92 /*.......................................................................
93 * Delete a PathName object.
95 * Input:
96 * path PathName * The object to be deleted.
97 * Output:
98 * return PathName * The deleted object (always NULL).
100 PathName *_del_PathName(PathName *path)
102 if(path) {
103 free(path->name);
104 free(path);
106 return NULL;
109 /*.......................................................................
110 * Return the pathname to a zero-length string.
112 * Input:
113 * path PathName * The pathname container.
114 * Output:
115 * return char * The cleared pathname buffer, or NULL on error.
117 char *_pn_clear_path(PathName *path)
120 * Check the arguments.
122 if(!path) {
123 errno = EINVAL;
124 return NULL;
126 path->name[0] = '\0';
127 return path->name;
130 /*.......................................................................
131 * Append a string to a pathname, increasing the size of the pathname
132 * buffer if needed.
134 * Input:
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
141 * string.
142 * remove_escapes int If true, remove the backslashes that escape
143 * spaces, tabs, backslashes etc..
144 * Output:
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,
150 int remove_escapes)
152 int pathlen; /* The length of the pathname */
153 int i;
155 * Check the arguments.
157 if(!path || !string) {
158 errno = EINVAL;
159 return NULL;
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))
174 return NULL;
176 * Append the string to the output pathname, removing any escape
177 * characters found therein.
179 if(remove_escapes) {
180 int is_escape = 0;
181 for(i=0; i<slen; i++) {
182 is_escape = !is_escape && string[i] == '\\';
183 if(!is_escape)
184 path->name[pathlen++] = string[i];
187 * Terminate the string.
189 path->name[pathlen] = '\0';
190 } else {
192 * Append the string directly to the pathname.
194 memcpy(path->name + pathlen, string, slen);
195 path->name[pathlen + slen] = '\0';
197 return path->name;
200 /*.......................................................................
201 * Prepend a string to a pathname, increasing the size of the pathname
202 * buffer if needed.
204 * Input:
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
211 * string.
212 * remove_escapes int If true, remove the backslashes that escape
213 * spaces, tabs, backslashes etc..
214 * Output:
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,
220 int remove_escapes)
222 int pathlen; /* The length of the pathname */
223 int shift; /* The number of characters to shift the suffix by */
224 int i,j;
226 * Check the arguments.
228 if(!path || !string) {
229 errno = EINVAL;
230 return NULL;
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
245 * been removed.
247 if(remove_escapes) {
248 int is_escape = 0;
249 for(shift=0,i=0; i<slen; i++) {
250 is_escape = !is_escape && string[i] == '\\';
251 if(!is_escape)
252 shift++;
254 } else {
255 shift = slen;
258 * Resize the pathname if needed.
260 if(!_pn_resize_path(path, pathlen + shift))
261 return NULL;
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.
270 if(remove_escapes) {
271 int is_escape = 0;
272 for(i=j=0; i<slen; i++) {
273 is_escape = !is_escape && string[i] == '\\';
274 if(!is_escape)
275 path->name[j++] = string[i];
277 } else {
278 memcpy(path->name, string, slen);
280 return path->name;
283 /*.......................................................................
284 * If needed reallocate a given pathname buffer to allow a string of
285 * a given length to be stored in it.
287 * Input:
288 * path PathName * The pathname container object.
289 * length size_t The required length of the pathname buffer,
290 * not including the terminating '\0'.
291 * Output:
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.
300 if(!path) {
301 errno = EINVAL;
302 return NULL;
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);
314 if(!name)
315 return NULL;
316 path->name = name;
317 path->dim = dim;
319 return path->name;
322 /*.......................................................................
323 * Estimate the largest amount of space needed to store a pathname.
325 * Output:
326 * return size_t The number of bytes needed, including space for the
327 * terminating '\0'.
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.
335 #ifdef PATH_MAX
336 maxlen = PATH_MAX;
338 * If we have pathconf, use it.
340 #elif defined(_PC_PATH_MAX)
341 errno = 0;
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
347 * guess.
349 #else
350 maxlen = MAX_PATHLEN_FALLBACK;
351 #endif
353 * Return the amount of space needed to accomodate a pathname plus
354 * a terminating '\0'.
356 return maxlen + 1;
359 /*.......................................................................
360 * Return non-zero if the specified path name refers to a directory.
362 * Input:
363 * pathname const char * The path to test.
364 * Output:
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)
375 return 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.
385 * Input:
386 * pathname const char * The path to test.
387 * Output:
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)
398 return 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.
408 * Input:
409 * pathname const char * The path to test.
410 * Output:
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)
421 return 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.
435 * Input:
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.
439 * Output:
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)
445 int i, j;
447 * Check the arguments.
449 if(!string || back_from < 0) {
450 errno = EINVAL;
451 return NULL;
454 * Search backwards from the specified index.
456 for(i=back_from-1; i>=0; i--) {
457 int c = string[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.
465 if(i==0)
466 break;
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)
477 break;
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.
488 * Input:
489 * string const char * The string to search backwards in.
490 * start_from int The index of the first character of the pathname
491 * in string[].
492 * Output:
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 */
500 int i;
502 * Check the arguments.
504 if(!string || start_from < 0) {
505 errno = EINVAL;
506 return NULL;
509 * Search forwards from the specified index.
511 for(i=start_from; (c=string[i]) != '\0'; i++) {
512 if(escaped) {
513 escaped = 0;
514 } else if(isspace(c)) {
515 break;
516 } else if(c == '\\') {
517 escaped = 1;
520 return (char *)string + i;
523 /*.......................................................................
524 * Return non-zero if the specified path name refers to an existing file.
526 * Input:
527 * pathname const char * The path to test.
528 * Output:
529 * return int 0 - The file doesn't exist.
530 * 1 - The file does exist.
532 int _pu_file_exists(const char *pathname)
534 struct stat statbuf;
535 return stat(pathname, &statbuf) == 0;
538 #endif /* ifndef WITHOUT_FILE_SYSTEM */