aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2024-06-15 11:42:48 -0700
committer3gg <3gg@shellblade.net>2024-06-15 11:42:48 -0700
commit04e3ded4c28c0b559620609daaae7b939d776b61 (patch)
tree55efea02bee351ac01ef764a04105ee9cee69259
parent993424547df0d253d546dbe7adee9b2448294b08 (diff)
Add path.
-rw-r--r--filesystem/CMakeLists.txt6
-rw-r--r--filesystem/include/filesystem.h6
-rw-r--r--filesystem/include/path.h38
-rw-r--r--filesystem/src/filesystem.c40
-rw-r--r--filesystem/src/path.c104
5 files changed, 148 insertions, 46 deletions
diff --git a/filesystem/CMakeLists.txt b/filesystem/CMakeLists.txt
index 55430e6..04390f4 100644
--- a/filesystem/CMakeLists.txt
+++ b/filesystem/CMakeLists.txt
@@ -3,9 +3,13 @@ cmake_minimum_required(VERSION 3.0)
3project(filesystem) 3project(filesystem)
4 4
5add_library(filesystem 5add_library(filesystem
6 src/filesystem.c) 6 src/filesystem.c
7 src/path.c)
7 8
8target_include_directories(filesystem PUBLIC 9target_include_directories(filesystem PUBLIC
9 include) 10 include)
10 11
12target_link_libraries(filesystem PRIVATE
13 cassert)
14
11target_compile_options(filesystem PRIVATE -Wall -Wextra) 15target_compile_options(filesystem PRIVATE -Wall -Wextra)
diff --git a/filesystem/include/filesystem.h b/filesystem/include/filesystem.h
index 3dce87f..1c354b7 100644
--- a/filesystem/include/filesystem.h
+++ b/filesystem/include/filesystem.h
@@ -3,7 +3,6 @@
3 */ 3 */
4#pragma once 4#pragma once
5 5
6#include <stdbool.h>
7#include <stddef.h> 6#include <stddef.h>
8#include <stdio.h> 7#include <stdio.h>
9 8
@@ -12,8 +11,3 @@ size_t get_file_size(FILE* file);
12 11
13/// Read the entire contents of the file into memory. 12/// Read the entire contents of the file into memory.
14void* read_file(const char* filepath); 13void* read_file(const char* filepath);
15
16/// Make a path relative to the parent directory of a file.
17bool make_relative_path(
18 const char* filepath, const char* path, char* relative,
19 size_t relative_length);
diff --git a/filesystem/include/path.h b/filesystem/include/path.h
new file mode 100644
index 0000000..8ad885d
--- /dev/null
+++ b/filesystem/include/path.h
@@ -0,0 +1,38 @@
1#pragma once
2
3#include <assert.h>
4#include <stdbool.h>
5#include <stddef.h>
6
7typedef struct path {
8 char* data;
9 size_t size; // Does not include null terminator. 0 if data is null.
10} path;
11
12/// Create a new path.
13path path_new(const char*);
14
15/// Free the path's memory.
16void path_del(path*);
17
18/// Return a C string from the path.
19static inline const char* path_cstr(path p) { return p.data; }
20
21/// Return true if the path is empty, false otherwise.
22static inline bool path_empty(path p) {
23 assert((p.data != 0) || (p.size == 0)); // null data -> 0 size
24 return p.data != 0;
25}
26
27/// Returns the parent directory, or empty path if the given path has no parent.
28/// The returned path is a slice of the input path; this function does not
29/// allocate.
30path path_parent_dir(path);
31
32/// Concatenate two paths.
33path path_concat(path left, path right);
34
35/// Make a path relative to the parent directory of a file.
36bool path_make_relative(
37 const char* filepath, const char* path, char* relative,
38 size_t relative_length);
diff --git a/filesystem/src/filesystem.c b/filesystem/src/filesystem.c
index f6bb693..b228e85 100644
--- a/filesystem/src/filesystem.c
+++ b/filesystem/src/filesystem.c
@@ -1,6 +1,7 @@
1#include <filesystem.h> 1#include <filesystem.h>
2 2
3#include <assert.h> 3#include <assert.h>
4#include <stdio.h>
4#include <stdlib.h> 5#include <stdlib.h>
5#include <string.h> 6#include <string.h>
6 7
@@ -54,42 +55,3 @@ cleanup:
54 } 55 }
55 return 0; 56 return 0;
56} 57}
57
58bool make_relative_path(
59 const char* filepath, const char* path, char* relative,
60 size_t relative_length) {
61 assert(filepath);
62 assert(path);
63 assert(relative);
64
65 const size_t filepath_len = strlen(filepath);
66 const size_t path_len = strlen(path);
67 assert(filepath_len < relative_length);
68 assert(path_len < relative_length);
69
70 // Handle empty filepath.
71 if (filepath_len == 0) {
72 memcpy(relative, path, path_len);
73 return true;
74 }
75
76 // Search for the last / in the file path to get its parent directory.
77 assert(filepath_len > 0);
78 size_t tm_dir_len = 0;
79 for (tm_dir_len = strlen(filepath) - 1; tm_dir_len > 0; --tm_dir_len) {
80 if (filepath[tm_dir_len] == '/') {
81 break;
82 }
83 }
84 tm_dir_len++; // Preserve the backslash.
85
86 // Copy the file path where the parent dir ends.
87 // Make sure there is enough space in the output.
88 if ((tm_dir_len + path_len + 1) >= relative_length) {
89 return false;
90 }
91 memcpy(relative, filepath, tm_dir_len);
92 memcpy(&relative[tm_dir_len], path, path_len);
93
94 return true;
95}
diff --git a/filesystem/src/path.c b/filesystem/src/path.c
new file mode 100644
index 0000000..2ce5a04
--- /dev/null
+++ b/filesystem/src/path.c
@@ -0,0 +1,104 @@
1#include <path.h>
2
3#include <cassert.h>
4#include <stdlib.h>
5#include <string.h>
6
7static const path Empty = (path){0, 0};
8
9path path_new(const char* str) {
10 assert(str);
11 const size_t size = strlen(str);
12 if (size > 0) {
13 char* data = calloc(size + 1, sizeof(char)); // +1 for null
14 memcpy(data, str, size);
15 data[size] = 0;
16 return (path){data, size};
17 }
18 return Empty;
19}
20
21void path_del(path* path) {
22 if (path) {
23 free(path->data);
24 path->data = 0;
25 path->size = 0;
26 }
27}
28
29path path_parent_dir(path p) {
30 assert(p.data);
31
32 if (p.size == 0) {
33 return Empty;
34 }
35 size_t i = p.size - 1;
36 // If the path ends with '/', skip the characters.
37 while ((i > 0) && (p.data[i] == '/')) {
38 i--;
39 }
40 // Search backwards for the parent dir.
41 for (; i > 0; --i) {
42 if (p.data[i] == '/') {
43 return (path){p.data, i + 1};
44 }
45 }
46 return Empty; // No parent.
47}
48
49path path_concat(path left, path right) {
50 assert(left.data);
51 assert(right.data);
52
53 // +1 for separator.
54 const size_t out_size = left.size + right.size + 1;
55 // +1 for null.
56 char* out = calloc(out_size + 1, sizeof(char));
57 ASSERT(out);
58
59 memcpy(out, left.data, left.size);
60 out[left.size] = '/';
61 memcpy(out + left.size + 1, right.data, right.size);
62 out[out_size] = 0;
63
64 return (path){out, out_size};
65}
66
67bool path_make_relative(
68 const char* filepath, const char* path, char* relative,
69 size_t relative_length) {
70 assert(filepath);
71 assert(path);
72 assert(relative);
73
74 const size_t filepath_len = strlen(filepath);
75 const size_t path_len = strlen(path);
76 assert(filepath_len < relative_length);
77 assert(path_len < relative_length);
78
79 // Handle empty filepath.
80 if (filepath_len == 0) {
81 memcpy(relative, path, path_len);
82 return true;
83 }
84
85 // Search for the last / in the file path to get its parent directory.
86 assert(filepath_len > 0);
87 size_t tm_dir_len = 0;
88 for (tm_dir_len = strlen(filepath) - 1; tm_dir_len > 0; --tm_dir_len) {
89 if (filepath[tm_dir_len] == '/') {
90 break;
91 }
92 }
93 tm_dir_len++; // Preserve the backslash.
94
95 // Copy the file path where the parent dir ends.
96 // Make sure there is enough space in the output.
97 if ((tm_dir_len + path_len + 1) >= relative_length) {
98 return false;
99 }
100 memcpy(relative, filepath, tm_dir_len);
101 memcpy(&relative[tm_dir_len], path, path_len);
102
103 return true;
104}