aboutsummaryrefslogtreecommitdiffstats
path: root/src/filepath.c
blob: d0b68dea797c863ee45f9d6fc3e0b4acc7db7c60 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/* filepath.c
 *
 * Copyright (c) 2014 Tharre
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */

#include <stdbool.h>
#include <assert.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>

#include "util.h"
#define _FILENAME "build.c"
#include "dbg.h"


/* Check if the given path is absolute. */
bool is_absolute(const char* path) {
	return path[0] == '/';
}

/* Returns a new copy of str with the extension removed, where the extension is
   everything behind the last dot, including the dot. */
char *remove_ext(const char *str) {
	assert(str);
	size_t len;
	char *ret, *dot = NULL;

	for (len = 0; str[len]; ++len)
		if (str[len] == '.')
			dot = (char*) &str[len];

	if (dot) /* recalculate length to only reach just before the last dot */
		len = dot - str;

	ret = xmalloc(len+1);
	memcpy(ret, str, len);
	ret[len] = '\0';

	return ret;
}

/* Returns the extension of the target or the empty string if none was found. */
char *take_extension(const char *target) {
	assert(target);
	char *temp = strrchr(target, '.');
	if (temp)
		return temp;
	else
		return "";
}

/* Make one path relative to another i.e. some/path to some/path/a/b/c would
   yield a/b/c as result. Prints '.' if the 2 paths match. */
// TODO: nameing, requires absolute paths, doesn't MALLOC
const char *make_relative(const char *target, const char *to) {
	int i;
	for (i = 0; target[i] && to[i]; ++i)
		if (target[i] != to[i]) {
			/* the paths do not match */
			return to;
		}

	if (!target[i] && !to[i]) {
		/* both paths match completely */
		return ".";
	}

	/* skip any leading seperators */
	while (to[i] == '/')
		++i;

	return &to[i];
}

/* Transforms target into a safe filename, replacing all '/' with '!'. */
char *transform_path(const char *target) {
	char *ptr = (char*) target;
	size_t escape = 0, i = 0;
	while (*ptr++)
		if (*ptr == '!') escape++;

	ptr = xmalloc((ptr-target) + escape + 1);
	do {
		if (*target == '/')
			ptr[i++] = '!';
		else if (*target == '!') {
			ptr[i++] = '!';
			ptr[i++] = '!';
		} else
			ptr[i++] = *target;
	} while (*target++);

	ptr[i] = '\0';
	return ptr;
}

/* Sane and portable basename implementation. */
char *xbasename(const char *path) {
	assert(path);
	char *ptr = strrchr(path, '/');
	return ptr? ptr+1 : (char*) path;
}

/* Checks if target exists and prints a debug message if access() failed
   except if it failed with ENOENT. */
bool fexists(const char *target) {
	assert(target);
	if (!access(target, F_OK))
		return true;
	if (errno != ENOENT)
		debug("Failed to access %s: %s\n", target, strerror(errno));
	return false;
}

/* Returns the size of fn, or -1 if the file doesn't exist. */
off_t fsize(const char *fn) {
	struct stat st;
	if (stat(fn, &st)) {
		if (errno != ENOENT)
			fatal("redo: failed to aquire stat() information about %s", fn);
		return -1;
	}

	return st.st_size;
}

/* Create the directory dir, while removing other 'files' with the same name.
   Returns true if the directory had to be created or false if it existed */
// TODO: fix confusing name
bool mkdirp(const char *dir) {
	struct stat st;
	if (stat(dir, &st)) {
		/* dir doesn't exist or stat failed */
		if (errno != ENOENT)
			fatal("redo: failed to aquire stat() information about %s", dir);
		if (mkdir(dir, 0755))
			fatal("redo: failed to mkdir() '%s'", dir);
		return 1;
	} else {
		if (!S_ISDIR(st.st_mode)) {
			if (remove(dir))
				fatal("redo: failed to remove %s", dir);
			if (mkdir(dir, 0755))
				fatal("redo: failed to mkdir() '%s'", dir);
			return 1;
		}
		return 0;
	}
}