8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / lib / libtecla / common / pathutil.c
blob60eded03b9b299b5237fce39fccf0c6a9f4759ff
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.
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
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <errno.h>
43 #include <string.h>
44 #include <ctype.h>
45 #include <limits.h>
47 #include <unistd.h>
48 #include <sys/types.h>
49 #include <sys/stat.h>
51 #include "pathutil.h"
53 /*.......................................................................
54 * Create a new PathName object.
56 * Output:
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));
66 if(!path) {
67 errno = ENOMEM;
68 return NULL;
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
73 * to _del_PathName().
75 path->name = NULL;
76 path->dim = 0;
78 * Figure out the maximum length of an expanded pathname.
80 path->dim = _pu_pathname_dim();
81 if(path->dim == 0)
82 return _del_PathName(path);
84 * Allocate the pathname buffer.
86 path->name = (char *)malloc(path->dim * sizeof(char));
87 if(!path->name) {
88 errno = ENOMEM;
89 return _del_PathName(path);
91 return path;
94 /*.......................................................................
95 * Delete a PathName object.
97 * Input:
98 * path PathName * The object to be deleted.
99 * Output:
100 * return PathName * The deleted object (always NULL).
102 PathName *_del_PathName(PathName *path)
104 if(path) {
105 if(path->name)
106 free(path->name);
107 free(path);
109 return NULL;
112 /*.......................................................................
113 * Return the pathname to a zero-length string.
115 * Input:
116 * path PathName * The pathname container.
117 * Output:
118 * return char * The cleared pathname buffer, or NULL on error.
120 char *_pn_clear_path(PathName *path)
123 * Check the arguments.
125 if(!path) {
126 errno = EINVAL;
127 return NULL;
129 path->name[0] = '\0';
130 return path->name;
133 /*.......................................................................
134 * Append a string to a pathname, increasing the size of the pathname
135 * buffer if needed.
137 * Input:
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
144 * string.
145 * remove_escapes int If true, remove the backslashes that escape
146 * spaces, tabs, backslashes etc..
147 * Output:
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,
153 int remove_escapes)
155 int pathlen; /* The length of the pathname */
156 int i;
158 * Check the arguments.
160 if(!path || !string) {
161 errno = EINVAL;
162 return NULL;
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))
177 return NULL;
179 * Append the string to the output pathname, removing any escape
180 * characters found therein.
182 if(remove_escapes) {
183 int is_escape = 0;
184 for(i=0; i<slen; i++) {
185 is_escape = !is_escape && string[i] == '\\';
186 if(!is_escape)
187 path->name[pathlen++] = string[i];
190 * Terminate the string.
192 path->name[pathlen] = '\0';
193 } else {
195 * Append the string directly to the pathname.
197 memcpy(path->name + pathlen, string, slen);
198 path->name[pathlen + slen] = '\0';
200 return path->name;
203 /*.......................................................................
204 * Prepend a string to a pathname, increasing the size of the pathname
205 * buffer if needed.
207 * Input:
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
214 * string.
215 * remove_escapes int If true, remove the backslashes that escape
216 * spaces, tabs, backslashes etc..
217 * Output:
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,
223 int remove_escapes)
225 int pathlen; /* The length of the pathname */
226 int shift; /* The number of characters to shift the suffix by */
227 int i,j;
229 * Check the arguments.
231 if(!path || !string) {
232 errno = EINVAL;
233 return NULL;
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
248 * been removed.
250 if(remove_escapes) {
251 int is_escape = 0;
252 for(shift=0,i=0; i<slen; i++) {
253 is_escape = !is_escape && string[i] == '\\';
254 if(!is_escape)
255 shift++;
257 } else {
258 shift = slen;
261 * Resize the pathname if needed.
263 if(!_pn_resize_path(path, pathlen + shift))
264 return NULL;
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.
273 if(remove_escapes) {
274 int is_escape = 0;
275 for(i=j=0; i<slen; i++) {
276 is_escape = !is_escape && string[i] == '\\';
277 if(!is_escape)
278 path->name[j++] = string[i];
280 } else {
281 memcpy(path->name, string, slen);
283 return path->name;
286 /*.......................................................................
287 * If needed reallocate a given pathname buffer to allow a string of
288 * a given length to be stored in it.
290 * Input:
291 * path PathName * The pathname container object.
292 * length size_t The required length of the pathname buffer,
293 * not including the terminating '\0'.
294 * Output:
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.
303 if(!path) {
304 errno = EINVAL;
305 return NULL;
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);
317 if(!name)
318 return NULL;
319 path->name = name;
320 path->dim = dim;
322 return path->name;
325 /*.......................................................................
326 * Estimate the largest amount of space needed to store a pathname.
328 * Output:
329 * return size_t The number of bytes needed, including space for the
330 * terminating '\0'.
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.
338 #ifdef PATH_MAX
339 maxlen = PATH_MAX;
341 * If we have pathconf, use it.
343 #elif defined(_PC_PATH_MAX)
344 errno = 0;
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
350 * guess.
352 #else
353 maxlen = MAX_PATHLEN_FALLBACK;
354 #endif
356 * Return the amount of space needed to accomodate a pathname plus
357 * a terminating '\0'.
359 return maxlen + 1;
362 /*.......................................................................
363 * Return non-zero if the specified path name refers to a directory.
365 * Input:
366 * pathname const char * The path to test.
367 * Output:
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)
378 return 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.
388 * Input:
389 * pathname const char * The path to test.
390 * Output:
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)
401 return 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.
411 * Input:
412 * pathname const char * The path to test.
413 * Output:
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)
424 return 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.
438 * Input:
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.
442 * Output:
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)
448 int i, j;
450 * Check the arguments.
452 if(!string || back_from < 0) {
453 errno = EINVAL;
454 return NULL;
457 * Search backwards from the specified index.
459 for(i=back_from-1; i>=0; i--) {
460 int c = string[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.
468 if(i==0)
469 break;
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)
480 break;
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.
491 * Input:
492 * string const char * The string to search backwards in.
493 * start_from int The index of the first character of the pathname
494 * in string[].
495 * Output:
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 */
503 int i;
505 * Check the arguments.
507 if(!string || start_from < 0) {
508 errno = EINVAL;
509 return NULL;
512 * Search forwards from the specified index.
514 for(i=start_from; (c=string[i]) != '\0'; i++) {
515 if(escaped) {
516 escaped = 0;
517 } else if(isspace(c)) {
518 break;
519 } else if(c == '\\') {
520 escaped = 1;
523 return (char *)string + i;
526 /*.......................................................................
527 * Return non-zero if the specified path name refers to an existing file.
529 * Input:
530 * pathname const char * The path to test.
531 * Output:
532 * return int 0 - The file doesn't exist.
533 * 1 - The file does exist.
535 int _pu_file_exists(const char *pathname)
537 struct stat statbuf;
538 return stat(pathname, &statbuf) == 0;
541 #endif /* ifndef WITHOUT_FILE_SYSTEM */