diff --git a/os/vfs/include/vfspaths.h b/os/vfs/include/vfspaths.h index 3a35b213d..6a11cbf05 100644 --- a/os/vfs/include/vfspaths.h +++ b/os/vfs/include/vfspaths.h @@ -56,6 +56,7 @@ extern "C" { #endif msg_t vfs_path_append(char *dst, const char *src); + msg_t vfs_path_normalize(char *dst, const char *src); #ifdef __cplusplus } #endif diff --git a/os/vfs/src/vfsbuffers.c b/os/vfs/src/vfsbuffers.c index e534c637e..c303b4dcf 100644 --- a/os/vfs/src/vfsbuffers.c +++ b/os/vfs/src/vfsbuffers.c @@ -67,7 +67,7 @@ static struct { /*===========================================================================*/ /** - * @brief VFS initialization. + * @brief VFS path buffers initialization. * * @init */ diff --git a/os/vfs/src/vfsparser.c b/os/vfs/src/vfsparser.c index 11a1ac82f..6a7ae0fa3 100644 --- a/os/vfs/src/vfsparser.c +++ b/os/vfs/src/vfsparser.c @@ -91,7 +91,8 @@ msg_t vfs_parse_match_end(const char **pathp) { /** * @brief Fetches the next path element. - * @note Consumes the next path separator, if any. + * @note Does not consume the next separator, if any. + * @note It can return an empty element, it has to be detected outside. * * @param[in, out] pathp pointer to the path under parsing * @param[out] fname extracted file name @@ -108,26 +109,21 @@ msg_t vfs_parse_get_fname(const char **pathp, char *fname) { /* Path elements must be terminated by a separator or an end-of-string.*/ if (vfs_parse_is_separator(c) || vfs_parse_is_terminator(c)) { - /* Consecutive separators are not valid.*/ - if (size == 0U) { - return VFS_RET_ENOENT; - } - /* Advancing the path pointer past the file name in the path and closing the file name string.*/ *pathp = p; *fname = '\0'; - return VFS_RET_SUCCESS; + return (msg_t)size; } /* Valid characters for path names.*/ if (!vfs_parse_is_filechar(c)) { - return VFS_RET_ENOENT; + return VFS_RET_EINVAL; } /* Exceeding the path element length.*/ if (size > VFS_CFG_NAMELEN_MAX) { - return VFS_RET_ENOENT; + return VFS_RET_ENAMETOOLONG; } *fname++ = c; diff --git a/os/vfs/src/vfspaths.c b/os/vfs/src/vfspaths.c index 21d789ca7..6d14533d2 100644 --- a/os/vfs/src/vfspaths.c +++ b/os/vfs/src/vfspaths.c @@ -101,4 +101,81 @@ msg_t vfs_path_append(char *dst, const char *src) { return VFS_RET_SUCCESS; } +/** + * @brief Normalizes an absolute path. + * + * @param[out] dst The destination buffer. + * @param[in] src The source path. + * @return The operation status. + * @retval VFS_RET_ENAMETOOLONG If the path size exceeded @p VFS_CFG_PATHLEN_MAX. + */ +msg_t vfs_path_normalize(char *dst, const char *src) { + size_t size; + + VFS_RETURN_ON_ERROR(vfs_parse_match_separator(&src)); + + *dst++ = '/'; + size = 1U; + while (true) { + + /* Consecutive input separators are consumed.*/ + while (vfs_parse_is_separator(*src)) { + src++; + } + msg_t ret; + size_t n; + + /* Getting next element from the input path and copying it to + the output path.*/ + ret = vfs_parse_get_fname(&src, dst); + VFS_RETURN_ON_ERROR(ret); + + n = (size_t)ret; + if (n == 0U) { + + /* If the path contains something after the root separator.*/ + if (size > 1U) { + /* No next path element, replacing the last separator with a zero.*/ + size--; + *(dst - 1) = '\0'; + } + + return (msg_t)size; + } + + /* Handling special cases of "." and "..".*/ + if (strcmp(dst, ".") == 0) { + /* Single dot elements are discarded.*/ + /* Consecutive input separators are consumed.*/ + continue; + } + else if (strcmp(dst, "..") == 0) { + /* Double dot elements require to remove the last element from + the output path.*/ + if (size > 1) { + /* Back on the separator.*/ + dst--; + size--; + + /* Scanning back to just after the previous separator.*/ + do { + dst--; + size--; + } while(!vfs_parse_is_separator(*(dst - 1))); + } + continue; + } + + dst += n; + size += n; + + /* Adding a single separator to the output.*/ + *dst++ = '/'; + size++; + + do { + } while (false); + } +} + /** @} */