Pharos 0.7.23
Modern Web Framework & Web App Hosting Service.
Loading...
Searching...
No Matches
native_support.c
Go to the documentation of this file.
1/**
2 * @file
3 * @brief Collects shared low-level helpers for buffers, strings, path manipulation, file reads, and simple path predicates.
4 */
5
6#include "native_support.h"
7
8#include <ctype.h>
9#include <limits.h>
10#include <stdint.h>
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14
15#ifdef _WIN32
16#include <direct.h>
17#include <io.h>
18#include <sys/stat.h>
19#define PHAROS_SUPPORT_PATH_SEP '\\'
20#define pharos_support_getcwd _getcwd
21#define pharos_support_access _access
22#define pharos_support_stat _stat
23#define pharos_support_strdup _strdup
24#else
25#include <unistd.h>
26#include <sys/stat.h>
27#define PHAROS_SUPPORT_PATH_SEP '/'
28#define pharos_support_getcwd getcwd
29#define pharos_support_access access
30#define pharos_support_stat stat
31#define pharos_support_strdup strdup
32#endif
33
34void buffer_init(Buffer *buffer) {
35 buffer->data = NULL;
36 buffer->len = 0;
37 buffer->cap = 0;
38}
39
40void buffer_free(Buffer *buffer) {
41 free(buffer->data);
42 buffer->data = NULL;
43 buffer->len = 0;
44 buffer->cap = 0;
45}
46
47int buffer_reserve(Buffer *buffer, size_t needed) {
48 char *next;
49 size_t cap = buffer->cap == 0 ? 4096 : buffer->cap;
50 if (needed <= buffer->cap) {
51 return 0;
52 }
53 while (cap < needed) {
54 if (cap > (SIZE_MAX / 2)) {
55 return -1;
56 }
57 cap *= 2;
58 }
59 next = (char *)realloc(buffer->data, cap);
60 if (next == NULL) {
61 return -1;
62 }
63 buffer->data = next;
64 buffer->cap = cap;
65 return 0;
66}
67
68int buffer_append(Buffer *buffer, const void *data, size_t len) {
69 if (buffer_reserve(buffer, buffer->len + len + 1) != 0) {
70 return -1;
71 }
72 memcpy(buffer->data + buffer->len, data, len);
73 buffer->len += len;
74 buffer->data[buffer->len] = '\0';
75 return 0;
76}
77
78char *dup_range(const char *start, size_t len) {
79 char *copy = (char *)malloc(len + 1);
80 if (copy == NULL) {
81 return NULL;
82 }
83 memcpy(copy, start, len);
84 copy[len] = '\0';
85 return copy;
86}
87
88void trim_in_place(char *text) {
89 char *start = text;
90 char *end;
91 if (text == NULL) {
92 return;
93 }
94 while (*start != '\0' && isspace((unsigned char)*start)) {
95 start++;
96 }
97 if (start != text) {
98 memmove(text, start, strlen(start) + 1);
99 }
100 end = text + strlen(text);
101 while (end > text && isspace((unsigned char)end[-1])) {
102 end--;
103 }
104 *end = '\0';
105}
106
107int starts_with(const char *value, const char *prefix) {
108 return strncmp(value, prefix, strlen(prefix)) == 0;
109}
110
111int streq(const char *a, const char *b) {
112 return strcmp(a, b) == 0;
113}
114
115char *path_join(const char *left, const char *right) {
116 size_t left_len;
117 size_t right_len;
118 int need_sep = 0;
119 char *joined;
120 if (left == NULL || left[0] == '\0') {
121 return pharos_support_strdup(right);
122 }
123 if (right == NULL || right[0] == '\0') {
124 return pharos_support_strdup(left);
125 }
126 left_len = strlen(left);
127 right_len = strlen(right);
128 if (left[left_len - 1] != PHAROS_SUPPORT_PATH_SEP) {
129 need_sep = 1;
130 }
131 joined = (char *)malloc(left_len + right_len + need_sep + 1);
132 if (joined == NULL) {
133 return NULL;
134 }
135 memcpy(joined, left, left_len);
136 if (need_sep) {
137 joined[left_len] = PHAROS_SUPPORT_PATH_SEP;
138 memcpy(joined + left_len + 1, right, right_len + 1);
139 } else {
140 memcpy(joined + left_len, right, right_len + 1);
141 }
142 return joined;
143}
144
145char *path_dirname(const char *path) {
146 const char *slash;
147 if (path == NULL) {
148 return pharos_support_strdup(".");
149 }
150 slash = strrchr(path, PHAROS_SUPPORT_PATH_SEP);
151#ifdef _WIN32
152 {
153 const char *alt = strrchr(path, '/');
154 if (alt != NULL && (slash == NULL || alt > slash)) {
155 slash = alt;
156 }
157 }
158#endif
159 if (slash == NULL) {
160 return pharos_support_strdup(".");
161 }
162 if (slash == path) {
163 return dup_range(path, 1);
164 }
165 return dup_range(path, (size_t)(slash - path));
166}
167
168int path_exists(const char *path) {
169 return path != NULL && pharos_support_access(path, F_OK) == 0;
170}
171
172int path_is_directory(const char *path) {
173#ifdef _WIN32
174 struct _stat st;
175#else
176 struct stat st;
177#endif
178 if (path == NULL) {
179 return 0;
180 }
181 if (pharos_support_stat(path, &st) != 0) {
182 return 0;
183 }
184#ifdef _WIN32
185 return (st.st_mode & _S_IFDIR) != 0;
186#else
187 return S_ISDIR(st.st_mode);
188#endif
189}
190
191int path_is_writable(const char *path) {
192 return path != NULL && pharos_support_access(path, W_OK) == 0;
193}
194
195char *path_canonicalize(const char *path) {
196 Buffer buffer;
197 const char *cursor;
198 int absolute;
199 if (path == NULL || path[0] == '\0') {
200 return NULL;
201 }
202 absolute = path[0] == PHAROS_SUPPORT_PATH_SEP;
203 buffer_init(&buffer);
204 if (absolute) {
205 char sep = PHAROS_SUPPORT_PATH_SEP;
206 if (buffer_append(&buffer, &sep, 1) != 0) {
207 buffer_free(&buffer);
208 return NULL;
209 }
210 }
211 cursor = path;
212 while (*cursor != '\0') {
213 const char *segment_start;
214 const char *segment_end;
215 size_t segment_len;
216 char *previous;
217 while (*cursor == PHAROS_SUPPORT_PATH_SEP) {
218 cursor++;
219 }
220 if (*cursor == '\0') {
221 break;
222 }
223 segment_start = cursor;
224 while (*cursor != '\0' && *cursor != PHAROS_SUPPORT_PATH_SEP) {
225 cursor++;
226 }
227 segment_end = cursor;
228 segment_len = (size_t)(segment_end - segment_start);
229 if (segment_len == 1 && segment_start[0] == '.') {
230 continue;
231 }
232 if (segment_len == 2 && segment_start[0] == '.' && segment_start[1] == '.') {
233 if (buffer.len > 0) {
234 size_t trim_to = buffer.len;
235 if (trim_to > 1 && buffer.data[trim_to - 1] == PHAROS_SUPPORT_PATH_SEP) {
236 trim_to--;
237 }
238 while (trim_to > 0 && buffer.data[trim_to - 1] != PHAROS_SUPPORT_PATH_SEP) {
239 trim_to--;
240 }
241 if (trim_to > 0) {
242 if (absolute && trim_to == 1) {
243 buffer.len = 1;
244 buffer.data[1] = '\0';
245 } else {
246 buffer.len = trim_to > 0 ? trim_to - (absolute ? 0 : 1) : 0;
247 if (buffer.len > 0 && buffer.data[buffer.len - 1] == PHAROS_SUPPORT_PATH_SEP && !(absolute && buffer.len == 1)) {
248 buffer.len--;
249 }
250 buffer.data[buffer.len] = '\0';
251 }
252 continue;
253 }
254 }
255 if (!absolute) {
256 if (buffer.len > 0) {
257 char sep = PHAROS_SUPPORT_PATH_SEP;
258 if (buffer_append(&buffer, &sep, 1) != 0) {
259 buffer_free(&buffer);
260 return NULL;
261 }
262 }
263 if (buffer_append(&buffer, segment_start, segment_len) != 0) {
264 buffer_free(&buffer);
265 return NULL;
266 }
267 }
268 continue;
269 }
270 if (buffer.len > 0 && buffer.data[buffer.len - 1] != PHAROS_SUPPORT_PATH_SEP) {
271 char sep = PHAROS_SUPPORT_PATH_SEP;
272 if (buffer_append(&buffer, &sep, 1) != 0) {
273 buffer_free(&buffer);
274 return NULL;
275 }
276 }
277 if (buffer_append(&buffer, segment_start, segment_len) != 0) {
278 buffer_free(&buffer);
279 return NULL;
280 }
281 previous = buffer.data;
282 (void)previous;
283 }
284 if (buffer.data == NULL) {
285 return absolute ? pharos_support_strdup("/") : pharos_support_strdup(".");
286 }
287 if (buffer.len == 0) {
288 buffer_free(&buffer);
289 return absolute ? pharos_support_strdup("/") : pharos_support_strdup(".");
290 }
291 return buffer.data;
292}
293
294char *resolve_relative_to(const char *base_dir, const char *value) {
295 if (value == NULL || value[0] == '\0') {
296 return NULL;
297 }
298#ifdef _WIN32
299 if ((strlen(value) > 2 && value[1] == ':') || value[0] == '\\' || value[0] == '/') {
300 return pharos_support_strdup(value);
301 }
302#else
303 if (value[0] == '/') {
304 return pharos_support_strdup(value);
305 }
306#endif
307 return path_join(base_dir, value);
308}
309
311 char stack_buf[PATH_MAX];
312 if (pharos_support_getcwd(stack_buf, sizeof(stack_buf)) == NULL) {
313 return pharos_support_strdup(".");
314 }
315 return pharos_support_strdup(stack_buf);
316}
317
318char *read_file_text(const char *path) {
319 Buffer buffer;
320 FILE *file;
321 char chunk[4096];
322 size_t bytes_read;
323 buffer_init(&buffer);
324 file = fopen(path, "rb");
325 if (file == NULL) {
326 return NULL;
327 }
328 while ((bytes_read = fread(chunk, 1, sizeof(chunk), file)) > 0) {
329 if (buffer_append(&buffer, chunk, bytes_read) != 0) {
330 fclose(file);
331 buffer_free(&buffer);
332 return NULL;
333 }
334 }
335 fclose(file);
336 return buffer.data;
337}
void buffer_init(Buffer *buffer)
Initializes a growable buffer to an empty state.
char * path_join(const char *left, const char *right)
Joins two path segments using the native path separator.
void trim_in_place(char *text)
Trims leading and trailing ASCII whitespace from a string in place.
char * dup_range(const char *start, size_t len)
Duplicates a byte range into a newly allocated NUL-terminated string.
char * path_canonicalize(const char *path)
Normalizes a path by collapsing redundant separators and dot segments.
void buffer_free(Buffer *buffer)
Releases memory owned by a growable buffer.
char * current_working_directory(void)
Returns the current working directory.
int streq(const char *a, const char *b)
Tests whether two strings are exactly equal.
int path_is_directory(const char *path)
Tests whether a filesystem path refers to a directory.
char * path_dirname(const char *path)
Returns the parent directory component of a path.
char * resolve_relative_to(const char *base_dir, const char *value)
Resolves a path value relative to a base directory when needed.
int buffer_append(Buffer *buffer, const void *data, size_t len)
Appends bytes to a growable buffer and maintains a trailing NUL byte.
#define pharos_support_access
int buffer_reserve(Buffer *buffer, size_t needed)
Ensures a buffer can hold at least a requested number of bytes.
int path_is_writable(const char *path)
Tests whether a filesystem path is writable by the current process.
#define pharos_support_getcwd
#define pharos_support_stat
#define PHAROS_SUPPORT_PATH_SEP
#define pharos_support_strdup
char * read_file_text(const char *path)
Reads an entire file into a newly allocated text buffer.
int starts_with(const char *value, const char *prefix)
Tests whether one string starts with another string.
int path_exists(const char *path)
Tests whether a filesystem path currently exists.
Declares shared low-level support helpers used across the Pharos native runtime.
Growable byte buffer used by parsing and string assembly helpers.
size_t len
size_t cap
char * data
const char size_t len
Definition yyjson.h:8364