/* * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include <unistd.h> #include <errno.h> #include <string.h> #include <stdlib.h> #include <assert.h> #include <sys/param.h> /* realpath logic: * 1. prepend CWD (/) * 2. iterate over components (search until next '/' or end of line) * - empty, skip the component * - if it is '.', skip the component * - if it is '..' * - and out_level == 0, ??? ('/..') * - otherwise, reverse-search for '/', set out_pos to that - 1, decrement out_level * - otherwise, add the component to output, increment out_level */ char * realpath(const char *file_name, char *resolved_name) { char * out_path = resolved_name; if (out_path == NULL) { /* allowed as an extension, allocate memory for the output path */ out_path = malloc(PATH_MAX); if (out_path == NULL) { errno = ENOMEM; return NULL; } } /* canonical path starts with / */ strlcpy(out_path, "/", PATH_MAX); /* pointers moving over the input and output path buffers */ const char* in_ptr = file_name; char* out_ptr = out_path + 1; /* number of path components in the output buffer */ size_t out_depth = 0; while (*in_ptr) { /* "path component" is the part between two '/' path separators. * locate the next path component in the input path: */ const char* end_of_path_component = strchrnul(in_ptr, '/'); size_t path_component_len = end_of_path_component - in_ptr; if (path_component_len == 0 || (path_component_len == 1 && in_ptr[0] == '.')) { /* empty path component or '.' - nothing to do */ } else if (path_component_len == 2 && in_ptr[0] == '.' && in_ptr[1] == '.') { /* '..' - remove one path component from the output */ if (out_depth == 0) { /* nothing to remove */ } else if (out_depth == 1) { /* there is only one path component in output; * remove it, but keep the leading separator */ out_ptr = out_path + 1; *out_ptr = '\0'; out_depth = 0; } else { /* remove last path component and the separator preceding it */ char * prev_sep = strrchr(out_path, '/'); assert(prev_sep > out_path); /* this shouldn't be the leading separator */ out_ptr = prev_sep; *out_ptr = '\0'; --out_depth; } } else { /* copy path component to output; +1 is for the separator */ if (out_ptr - out_path + 1 + path_component_len > PATH_MAX - 1) { /* output buffer insufficient */ errno = E2BIG; goto fail; } else { /* add separator if necessary */ if (out_depth > 0) { *out_ptr = '/'; ++out_ptr; } memcpy(out_ptr, in_ptr, path_component_len); out_ptr += path_component_len; *out_ptr = '\0'; ++out_depth; } } /* move input pointer to separator right after this path component */ in_ptr += path_component_len; if (*in_ptr != '\0') { /* move past it unless already at the end of the input string */ ++in_ptr; } } return out_path; fail: if (resolved_name == NULL) { /* out_path was allocated, free it */ free(out_path); } return NULL; } char * getcwd(char *buf, size_t size) { if (buf == NULL) { return strdup("/"); } strlcpy(buf, "/", size); return buf; } int chdir(const char *path) { (void) path; errno = ENOSYS; return -1; }