aboutsummaryrefslogtreecommitdiff
path: root/lib/functions.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/functions.c')
-rw-r--r--lib/functions.c120
1 files changed, 107 insertions, 13 deletions
diff --git a/lib/functions.c b/lib/functions.c
index 44f21b55..0b003fd6 100644
--- a/lib/functions.c
+++ b/lib/functions.c
@@ -93,7 +93,7 @@ void xrealloc(void **ptr, size_t size)
if (!*ptr) error_exit("xrealloc");
}
-// Die unless we can allocate a copy of this string.
+// Die unless we can allocate a copy of this many bytes of string.
void *xstrndup(char *s, size_t n)
{
void *ret = xmalloc(++n);
@@ -102,6 +102,12 @@ void *xstrndup(char *s, size_t n)
return ret;
}
+// Die unless we can allocate a copy of this string.
+void *xstrdup(char *s)
+{
+ return xstrndup(s,strlen(s));
+}
+
// Die unless we can allocate enough space to sprintf() into.
char *xmsprintf(char *format, ...)
{
@@ -188,29 +194,117 @@ char *xgetcwd(void)
return buf;
}
-// Find this file in a colon-separated path.
+// Cannonicalizes path by removing ".", "..", and "//" elements. This is not
+// the same as realpath(), where "dir/.."
+char *xabspath(char *path)
+{
+ char *from, *to;
+
+ // If this isn't an absolute path, make it one with cwd.
+ if (path[0]!='/') {
+ char *cwd=xgetcwd();
+ path = xmsprintf("%s/%s",cwd,path);
+ free(cwd);
+ } else path = xstrdup(path);
+
+ // Loop through path elements
+ from = to = path;
+ while (*from) {
+
+ // Continue any current path component.
+ if (*from!='/') {
+ *(to++) = *(from++);
+ continue;
+ }
+
+ // Skip duplicate slashes.
+ while (*from=='/') from++;
+
+ // Start of a new filename. Handle . and ..
+ while (*from=='.') {
+ // Skip .
+ if (from[1]=='/') from += 2;
+ else if (!from[1]) from++;
+ // Back up for ..
+ else if (from[1]=='.') {
+ if (from[2]=='/') from +=3;
+ else if(!from[2]) from+=2;
+ else break;
+ while (to>path && *(--to)!='/');
+ } else break;
+ }
+ // Add directory separator slash.
+ *(to++) = '/';
+ }
+ *to = 0;
+
+ return path;
+}
-char *find_in_path(char *path, char *filename)
+// Check whether a file exists, or is executable, or...
+int is_file_type(char *path, int type)
{
- char *next, *res = NULL, *cwd = xgetcwd();
+ // Is there a file here we can execute?
+ if (!access(path, type)) {
+ struct stat st;
+ // Confirm it's not a directory.
+ if (!stat(path, &st) && S_ISREG(st.st_mode)) return 1;
+ }
+
+ return 0;
+}
- while ((next = index(path,':'))) {
- int len = next-path;
- if (len==1) res = xmsprintf("%s/%s", cwd, filename);
- else res = xmsprintf("%*s/%s",len-1,path,filename);
- // Is there a file here we can execute?
- if (!access(res, X_OK)) {
- struct stat st;
- // Confirm it's not a directory.
- if (!stat(res, &st) && S_ISREG(st.st_mode)) break;
+// Find an exectuable file either at a path with a slash in it (absolute or
+// relative to current directory), or in $PATH. Returns absolute path to file,
+// or NULL if not found.
+
+
+char *which_in_path(char *filename)
+{
+ char *res;
+
+ if (index(filename, '/')) {
+ res = xabspath(filename);
+ if (is_file_type(filename, X_OK)) return res;
+ free(res);
+ return NULL;
+ }
+ return find_in_path(getenv("PATH"), filename, X_OK);
+}
+
+// Find file in a colon-separated path with access type "type" (generally
+// X_OK or R_OK). Returns absolute path to file, or NULL if not found.
+
+char *find_in_path(char *path, char *filename, int type)
+{
+ char *res = NULL, *cwd = xgetcwd();
+
+ for (;;) {
+ char *next = path ? index(path, ':') : NULL;
+ int len = next ? next-path : strlen(path);
+
+ if (!len) res = xmsprintf("%s/%s", cwd, filename);
+ else {
+ res = xmalloc(len+strlen(filename)+2);
+ strncpy(res, path, len);
+ res[len] = '/';
+ strcpy(res+len+1, filename);
}
+
+ // Is there a file here we can execute?
+ if (is_file_type(res, type)) break;
+
free(res);
res = NULL;
+ if (!next) break;
+ path += len;
+ path++;
}
free(cwd);
return res;
+
}
// Convert unsigned int to ascii, writing into supplied buffer. A truncated