summaryrefslogtreecommitdiffstats
path: root/libtecla-1.6.3/pathutil.c
diff options
context:
space:
mode:
Diffstat (limited to 'libtecla-1.6.3/pathutil.c')
-rw-r--r--libtecla-1.6.3/pathutil.c539
1 files changed, 539 insertions, 0 deletions
diff --git a/libtecla-1.6.3/pathutil.c b/libtecla-1.6.3/pathutil.c
new file mode 100644
index 0000000..be14484
--- /dev/null
+++ b/libtecla-1.6.3/pathutil.c
@@ -0,0 +1,539 @@
+/*
+ * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 by Martin C. Shepherd.
+ *
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, and/or sell copies of the Software, and to permit persons
+ * to whom the Software is furnished to do so, provided that the above
+ * copyright notice(s) and this permission notice appear in all copies of
+ * the Software and that both the above copyright notice(s) and this
+ * permission notice appear in supporting documentation.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+ * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
+ * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
+ * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Except as contained in this notice, the name of a copyright holder
+ * shall not be used in advertising or otherwise to promote the sale, use
+ * or other dealings in this Software without prior written authorization
+ * of the copyright holder.
+ */
+
+/*
+ * If file-system access is to be excluded, this module has no function,
+ * so all of its code should be excluded.
+ */
+#ifndef WITHOUT_FILE_SYSTEM
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "pathutil.h"
+
+/*.......................................................................
+ * Create a new PathName object.
+ *
+ * Output:
+ * return PathName * The new object, or NULL on error.
+ */
+PathName *_new_PathName(void)
+{
+ PathName *path; /* The object to be returned */
+/*
+ * Allocate the container.
+ */
+ path = (PathName *) malloc(sizeof(PathName));
+ if(!path) {
+ errno = ENOMEM;
+ return NULL;
+ };
+/*
+ * Before attempting any operation that might fail, initialize the
+ * container at least up to the point at which it can safely be passed
+ * to _del_PathName().
+ */
+ path->name = NULL;
+ path->dim = 0;
+/*
+ * Figure out the maximum length of an expanded pathname.
+ */
+ path->dim = _pu_pathname_dim();
+ if(path->dim == 0)
+ return _del_PathName(path);
+/*
+ * Allocate the pathname buffer.
+ */
+ path->name = (char *)malloc(path->dim * sizeof(char));
+ if(!path->name) {
+ errno = ENOMEM;
+ return _del_PathName(path);
+ };
+ return path;
+}
+
+/*.......................................................................
+ * Delete a PathName object.
+ *
+ * Input:
+ * path PathName * The object to be deleted.
+ * Output:
+ * return PathName * The deleted object (always NULL).
+ */
+PathName *_del_PathName(PathName *path)
+{
+ if(path) {
+ if(path->name)
+ free(path->name);
+ free(path);
+ };
+ return NULL;
+}
+
+/*.......................................................................
+ * Return the pathname to a zero-length string.
+ *
+ * Input:
+ * path PathName * The pathname container.
+ * Output:
+ * return char * The cleared pathname buffer, or NULL on error.
+ */
+char *_pn_clear_path(PathName *path)
+{
+/*
+ * Check the arguments.
+ */
+ if(!path) {
+ errno = EINVAL;
+ return NULL;
+ };
+ path->name[0] = '\0';
+ return path->name;
+}
+
+/*.......................................................................
+ * Append a string to a pathname, increasing the size of the pathname
+ * buffer if needed.
+ *
+ * Input:
+ * path PathName * The pathname container.
+ * string const char * The string to be appended to the pathname.
+ * Note that regardless of the slen argument,
+ * this should be a '\0' terminated string.
+ * slen int The maximum number of characters to append
+ * from string[], or -1 to append the whole
+ * string.
+ * remove_escapes int If true, remove the backslashes that escape
+ * spaces, tabs, backslashes etc..
+ * Output:
+ * return char * The pathname string path->name[], which may
+ * have been reallocated, or NULL if there was
+ * insufficient memory to extend the pathname.
+ */
+char *_pn_append_to_path(PathName *path, const char *string, int slen,
+ int remove_escapes)
+{
+ int pathlen; /* The length of the pathname */
+ int i;
+/*
+ * Check the arguments.
+ */
+ if(!path || !string) {
+ errno = EINVAL;
+ return NULL;
+ };
+/*
+ * Get the current length of the pathname.
+ */
+ pathlen = strlen(path->name);
+/*
+ * How many characters should be appended?
+ */
+ if(slen < 0 || slen > strlen(string))
+ slen = strlen(string);
+/*
+ * Resize the pathname if needed.
+ */
+ if(!_pn_resize_path(path, pathlen + slen))
+ return NULL;
+/*
+ * Append the string to the output pathname, removing any escape
+ * characters found therein.
+ */
+ if(remove_escapes) {
+ int is_escape = 0;
+ for(i=0; i<slen; i++) {
+ is_escape = !is_escape && string[i] == '\\';
+ if(!is_escape)
+ path->name[pathlen++] = string[i];
+ };
+/*
+ * Terminate the string.
+ */
+ path->name[pathlen] = '\0';
+ } else {
+/*
+ * Append the string directly to the pathname.
+ */
+ memcpy(path->name + pathlen, string, slen);
+ path->name[pathlen + slen] = '\0';
+ };
+ return path->name;
+}
+
+/*.......................................................................
+ * Prepend a string to a pathname, increasing the size of the pathname
+ * buffer if needed.
+ *
+ * Input:
+ * path PathName * The pathname container.
+ * string const char * The string to be prepended to the pathname.
+ * Note that regardless of the slen argument,
+ * this should be a '\0' terminated string.
+ * slen int The maximum number of characters to prepend
+ * from string[], or -1 to append the whole
+ * string.
+ * remove_escapes int If true, remove the backslashes that escape
+ * spaces, tabs, backslashes etc..
+ * Output:
+ * return char * The pathname string path->name[], which may
+ * have been reallocated, or NULL if there was
+ * insufficient memory to extend the pathname.
+ */
+char *_pn_prepend_to_path(PathName *path, const char *string, int slen,
+ int remove_escapes)
+{
+ int pathlen; /* The length of the pathname */
+ int shift; /* The number of characters to shift the suffix by */
+ int i,j;
+/*
+ * Check the arguments.
+ */
+ if(!path || !string) {
+ errno = EINVAL;
+ return NULL;
+ };
+/*
+ * Get the current length of the pathname.
+ */
+ pathlen = strlen(path->name);
+/*
+ * How many characters should be appended?
+ */
+ if(slen < 0 || slen > strlen(string))
+ slen = strlen(string);
+/*
+ * Work out how far we need to shift the original path string to make
+ * way for the new prefix. When removing escape characters, we need
+ * final length of the new prefix, after unescaped backslashes have
+ * been removed.
+ */
+ if(remove_escapes) {
+ int is_escape = 0;
+ for(shift=0,i=0; i<slen; i++) {
+ is_escape = !is_escape && string[i] == '\\';
+ if(!is_escape)
+ shift++;
+ };
+ } else {
+ shift = slen;
+ };
+/*
+ * Resize the pathname if needed.
+ */
+ if(!_pn_resize_path(path, pathlen + shift))
+ return NULL;
+/*
+ * Make room for the prefix at the beginning of the string.
+ */
+ memmove(path->name + shift, path->name, pathlen+1);
+/*
+ * Copy the new prefix into the vacated space at the beginning of the
+ * output pathname, removing any escape characters if needed.
+ */
+ if(remove_escapes) {
+ int is_escape = 0;
+ for(i=j=0; i<slen; i++) {
+ is_escape = !is_escape && string[i] == '\\';
+ if(!is_escape)
+ path->name[j++] = string[i];
+ };
+ } else {
+ memcpy(path->name, string, slen);
+ };
+ return path->name;
+}
+
+/*.......................................................................
+ * If needed reallocate a given pathname buffer to allow a string of
+ * a given length to be stored in it.
+ *
+ * Input:
+ * path PathName * The pathname container object.
+ * length size_t The required length of the pathname buffer,
+ * not including the terminating '\0'.
+ * Output:
+ * return char * The pathname buffer, or NULL if there was
+ * insufficient memory.
+ */
+char *_pn_resize_path(PathName *path, size_t length)
+{
+/*
+ * Check the arguments.
+ */
+ if(!path) {
+ errno = EINVAL;
+ return NULL;
+ };
+/*
+ * If the pathname buffer isn't large enough to accomodate a string
+ * of the specified length, attempt to reallocate it with the new
+ * size, plus space for a terminating '\0'. Also add a bit of
+ * head room to prevent too many reallocations if the initial length
+ * turned out to be very optimistic.
+ */
+ if(length + 1 > path->dim) {
+ size_t dim = length + 1 + PN_PATHNAME_INC;
+ char *name = (char *) realloc(path->name, dim);
+ if(!name)
+ return NULL;
+ path->name = name;
+ path->dim = dim;
+ };
+ return path->name;
+}
+
+/*.......................................................................
+ * Estimate the largest amount of space needed to store a pathname.
+ *
+ * Output:
+ * return size_t The number of bytes needed, including space for the
+ * terminating '\0'.
+ */
+size_t _pu_pathname_dim(void)
+{
+ int maxlen; /* The return value excluding space for the '\0' */
+/*
+ * If the POSIX PATH_MAX macro is defined in limits.h, use it.
+ */
+#ifdef PATH_MAX
+ maxlen = PATH_MAX;
+/*
+ * If we have pathconf, use it.
+ */
+#elif defined(_PC_PATH_MAX)
+ errno = 0;
+ maxlen = pathconf(FS_ROOT_DIR, _PC_PATH_MAX);
+ if(maxlen <= 0 || errno)
+ maxlen = MAX_PATHLEN_FALLBACK;
+/*
+ * None of the above approaches worked, so substitute our fallback
+ * guess.
+ */
+#else
+ maxlen = MAX_PATHLEN_FALLBACK;
+#endif
+/*
+ * Return the amount of space needed to accomodate a pathname plus
+ * a terminating '\0'.
+ */
+ return maxlen + 1;
+}
+
+/*.......................................................................
+ * Return non-zero if the specified path name refers to a directory.
+ *
+ * Input:
+ * pathname const char * The path to test.
+ * Output:
+ * return int 0 - Not a directory.
+ * 1 - pathname[] refers to a directory.
+ */
+int _pu_path_is_dir(const char *pathname)
+{
+ struct stat statbuf; /* The file-statistics return buffer */
+/*
+ * Look up the file attributes.
+ */
+ if(stat(pathname, &statbuf) < 0)
+ return 0;
+/*
+ * Is the file a directory?
+ */
+ return S_ISDIR(statbuf.st_mode) != 0;
+}
+
+/*.......................................................................
+ * Return non-zero if the specified path name refers to a regular file.
+ *
+ * Input:
+ * pathname const char * The path to test.
+ * Output:
+ * return int 0 - Not a regular file.
+ * 1 - pathname[] refers to a regular file.
+ */
+int _pu_path_is_file(const char *pathname)
+{
+ struct stat statbuf; /* The file-statistics return buffer */
+/*
+ * Look up the file attributes.
+ */
+ if(stat(pathname, &statbuf) < 0)
+ return 0;
+/*
+ * Is the file a regular file?
+ */
+ return S_ISREG(statbuf.st_mode) != 0;
+}
+
+/*.......................................................................
+ * Return non-zero if the specified path name refers to an executable.
+ *
+ * Input:
+ * pathname const char * The path to test.
+ * Output:
+ * return int 0 - Not an executable file.
+ * 1 - pathname[] refers to an executable file.
+ */
+int _pu_path_is_exe(const char *pathname)
+{
+ struct stat statbuf; /* The file-statistics return buffer */
+/*
+ * Look up the file attributes.
+ */
+ if(stat(pathname, &statbuf) < 0)
+ return 0;
+/*
+ * Is the file a regular file which is executable by the current user.
+ */
+ return S_ISREG(statbuf.st_mode) != 0 &&
+ (statbuf.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)) &&
+ access(pathname, X_OK) == 0;
+}
+
+/*.......................................................................
+ * Search backwards for the potential start of a filename. This
+ * looks backwards from the specified index in a given string,
+ * stopping at the first unescaped space or the start of the line.
+ *
+ * Input:
+ * string const char * The string to search backwards in.
+ * back_from int The index of the first character in string[]
+ * that follows the pathname.
+ * Output:
+ * return char * The pointer to the first character of
+ * the potential pathname, or NULL on error.
+ */
+char *_pu_start_of_path(const char *string, int back_from)
+{
+ int i, j;
+/*
+ * Check the arguments.
+ */
+ if(!string || back_from < 0) {
+ errno = EINVAL;
+ return NULL;
+ };
+/*
+ * Search backwards from the specified index.
+ */
+ for(i=back_from-1; i>=0; i--) {
+ int c = string[i];
+/*
+ * Stop on unescaped spaces.
+ */
+ if(isspace((int)(unsigned char)c)) {
+/*
+ * The space can't be escaped if we are at the start of the line.
+ */
+ if(i==0)
+ break;
+/*
+ * Find the extent of the escape characters which precedes the space.
+ */
+ for(j=i-1; j>=0 && string[j]=='\\'; j--)
+ ;
+/*
+ * If there isn't an odd number of escape characters before the space,
+ * then the space isn't escaped.
+ */
+ if((i - 1 - j) % 2 == 0)
+ break;
+ };
+ };
+ return (char *)string + i + 1;
+}
+
+/*.......................................................................
+ * Find the length of a potential filename starting from a given
+ * point. This looks forwards from the specified index in a given string,
+ * stopping at the first unescaped space or the end of the line.
+ *
+ * Input:
+ * string const char * The string to search backwards in.
+ * start_from int The index of the first character of the pathname
+ * in string[].
+ * Output:
+ * return char * The pointer to the character that follows
+ * the potential pathname, or NULL on error.
+ */
+char *_pu_end_of_path(const char *string, int start_from)
+{
+ int c; /* The character being examined */
+ int escaped = 0; /* True when the next character is escaped */
+ int i;
+/*
+ * Check the arguments.
+ */
+ if(!string || start_from < 0) {
+ errno = EINVAL;
+ return NULL;
+ };
+/*
+ * Search forwards from the specified index.
+ */
+ for(i=start_from; (c=string[i]) != '\0'; i++) {
+ if(escaped) {
+ escaped = 0;
+ } else if(isspace(c)) {
+ break;
+ } else if(c == '\\') {
+ escaped = 1;
+ };
+ };
+ return (char *)string + i;
+}
+
+/*.......................................................................
+ * Return non-zero if the specified path name refers to an existing file.
+ *
+ * Input:
+ * pathname const char * The path to test.
+ * Output:
+ * return int 0 - The file doesn't exist.
+ * 1 - The file does exist.
+ */
+int _pu_file_exists(const char *pathname)
+{
+ struct stat statbuf;
+ return stat(pathname, &statbuf) == 0;
+}
+
+#endif /* ifndef WITHOUT_FILE_SYSTEM */