summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/fileops.c76
-rw-r--r--src/fileops.h24
2 files changed, 100 insertions, 0 deletions
diff --git a/src/fileops.c b/src/fileops.c
index 1d680f354..1c480e727 100644
--- a/src/fileops.c
+++ b/src/fileops.c
@@ -364,5 +364,81 @@ int gitfo_mkdir_recurs(const char *path, int mode)
free(path_copy);
return error;
+}
+
+static int retrieve_previous_path_component_start(const char *path)
+{
+ int offset, len, start = 0;
+
+ len = strlen(path);
+ offset = len - 1;
+
+ /* Skip leading slash */
+ if (path[start] == '/')
+ start++;
+
+ /* Skip trailing slash */
+ if (path[offset] == '/')
+ offset--;
+
+ if (offset < 0)
+ return GIT_ERROR;
+
+ while (offset > start && path[offset-1] != '/') {
+ offset--;
+ }
+
+ return offset;
+}
+
+int git_prettify_dir_path(char *buffer_out, const char *path)
+{
+ int len = 0;
+ char *current;
+ const char *buffer_out_start, *buffer_end;
+
+ buffer_out_start = buffer_out;
+ current = (char *)path;
+ buffer_end = path + strlen(path);
+
+ while (current < buffer_end) {
+ /* Prevent multiple slashes from being added to the output */
+ if (*current == '/' && len > 0 && buffer_out_start[len - 1] == '/') {
+ current++;
+ continue;
+ }
+
+ /* Skip current directory */
+ if (*current == '.') {
+ current++;
+
+ /* Handle the double-dot upward directory navigation */
+ if (*current == '.') {
+ current++;
+
+ *buffer_out ='\0';
+ len = retrieve_previous_path_component_start(buffer_out_start);
+ if (len < GIT_SUCCESS)
+ return GIT_ERROR;
+ buffer_out = (char *)buffer_out_start + len;
+ }
+
+ if (*current == '/')
+ current++;
+
+ continue;
+ }
+
+ *buffer_out++ = *current++;
+ len++;
+ }
+
+ /* Add a trailing slash if required */
+ if (len > 0 && buffer_out_start[len-1] != '/')
+ *buffer_out++ = '/';
+
+ *buffer_out = '\0';
+
+ return GIT_SUCCESS;
}
diff --git a/src/fileops.h b/src/fileops.h
index fa446e36a..d562dc7ed 100644
--- a/src/fileops.h
+++ b/src/fileops.h
@@ -131,4 +131,28 @@ extern int gitfo_write_cached(gitfo_cache *ioc, void *buf, size_t len);
extern int gitfo_flush_cached(gitfo_cache *ioc);
extern int gitfo_close_cached(gitfo_cache *ioc);
+/**
+ * Clean up a provided absolute or relative directory path.
+ *
+ * This prettification relies on basic operations such as coalescing
+ * multiple forward slashes into a single slash, removing '.' and
+ * './' current directory segments, and removing parent directory
+ * whenever '..' is encountered.
+ *
+ * If not empty, the returned path ends with a forward slash.
+ *
+ * For instance, this will turn "d1/s1///s2/..//../s3" into "d1/s3/".
+ *
+ * This only performs a string based analysis of the path.
+ * No checks are done to make sure the path actually makes sense from
+ * the file system perspective.
+ *
+ * @param buffer_out buffer to populate with the normalized path.
+ * @param path directory path to clean up.
+ * @return
+ * - GIT_SUCCESS on success;
+ * - GIT_ERROR when the input path is invalid or escapes the current directory.
+ */
+GIT_EXTERN(int) git_prettify_dir_path(char *buffer_out, const char *path);
+
#endif /* INCLUDE_fileops_h__ */