#define _POSIX_C_SOURCE 200809L #define _XOPEN_SOURCE 700 #include "Path/Path.h" #include #include #include static const char output_prefix[] = "yapssg_"; static int checked_add(size_t left, size_t right, size_t *sum) { if (left > SIZE_MAX - right) { return -1; } *sum = left + right; return 0; } static char *duplicate_string(const char *value) { size_t length = strlen(value); char *copy = malloc(length + 1); if (copy != NULL) { memcpy(copy, value, length + 1); } return copy; } char *path_join(const char *left, const char *right) { size_t left_length = strlen(left); size_t right_length = strlen(right); int needs_separator = left_length > 0 && left[left_length - 1] != '/'; size_t path_length; size_t allocation_size; char *path; if (checked_add(left_length, (size_t)needs_separator, &path_length) != 0 || checked_add(path_length, right_length, &path_length) != 0 || checked_add(path_length, 1, &allocation_size) != 0) { return NULL; } path = malloc(allocation_size); if (path == NULL) { return NULL; } memcpy(path, left, left_length); if (needs_separator) { path[left_length++] = '/'; } memcpy(path + left_length, right, right_length + 1); return path; } char *path_make_default_output(const char *input_path) { const char *end = input_path + strlen(input_path); const char *base; size_t parent_length; size_t base_length; size_t prefix_length = sizeof(output_prefix) - 1; size_t output_length; size_t allocation_size; char *output_path; while (end > input_path + 1 && end[-1] == '/') { --end; } base = end; while (base > input_path && base[-1] != '/') { --base; } parent_length = (size_t)(base - input_path); base_length = (size_t)(end - base); if (base_length == 0) { return NULL; } if (checked_add(parent_length, prefix_length, &output_length) != 0 || checked_add(output_length, base_length, &output_length) != 0 || checked_add(output_length, 1, &allocation_size) != 0) { return NULL; } output_path = malloc(allocation_size); if (output_path == NULL) { return NULL; } memcpy(output_path, input_path, parent_length); memcpy(output_path + parent_length, output_prefix, prefix_length); memcpy(output_path + parent_length + prefix_length, base, base_length); output_path[parent_length + prefix_length + base_length] = '\0'; return output_path; } char *path_resolve_new(const char *path) { char *path_copy = duplicate_string(path); char *slash; char *parent; char *resolved_parent; char *resolved_path; const char *base; size_t length; if (path_copy == NULL) { return NULL; } length = strlen(path_copy); while (length > 1 && path_copy[length - 1] == '/') { path_copy[--length] = '\0'; } slash = strrchr(path_copy, '/'); if (slash == NULL) { parent = duplicate_string("."); base = path_copy; } else if (slash == path_copy) { parent = duplicate_string("/"); base = slash + 1; } else { *slash = '\0'; parent = duplicate_string(path_copy); base = slash + 1; } if (parent == NULL || base[0] == '\0') { free(parent); free(path_copy); return NULL; } resolved_parent = realpath(parent, NULL); free(parent); if (resolved_parent == NULL) { free(path_copy); return NULL; } resolved_path = path_join(resolved_parent, base); free(resolved_parent); free(path_copy); return resolved_path; } char *path_resolve_from_file(const char *file_path, const char *relative_path) { const char *slash; size_t directory_length; size_t relative_length; size_t path_length; size_t allocation_size; char *path; if (relative_path[0] == '/') { return duplicate_string(relative_path); } slash = strrchr(file_path, '/'); directory_length = slash == NULL ? 0 : (size_t)(slash - file_path + 1); relative_length = strlen(relative_path); if (checked_add(directory_length, relative_length, &path_length) != 0 || checked_add(path_length, 1, &allocation_size) != 0) { return NULL; } path = malloc(allocation_size); if (path == NULL) { return NULL; } memcpy(path, file_path, directory_length); memcpy(path + directory_length, relative_path, relative_length + 1); return path; } int path_is_within(const char *parent, const char *candidate) { size_t parent_length = strlen(parent); if (parent_length == 1 && parent[0] == '/') { return candidate[0] == '/'; } return strncmp(parent, candidate, parent_length) == 0 && (candidate[parent_length] == '\0' || candidate[parent_length] == '/'); }