diff options
-rw-r--r-- | cache.h | 1 | ||||
-rw-r--r-- | symlinks.c | 48 |
2 files changed, 49 insertions, 0 deletions
@@ -410,6 +410,7 @@ struct checkout { }; extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath); +extern int has_symlink_leading_path(const char *name, char *last_symlink); extern struct alternate_object_database { struct alternate_object_database *next; diff --git a/symlinks.c b/symlinks.c new file mode 100644 index 0000000000..be9ace6c04 --- /dev/null +++ b/symlinks.c @@ -0,0 +1,48 @@ +#include "cache.h" + +int has_symlink_leading_path(const char *name, char *last_symlink) +{ + char path[PATH_MAX]; + const char *sp, *ep; + char *dp; + + sp = name; + dp = path; + + if (last_symlink && *last_symlink) { + size_t last_len = strlen(last_symlink); + size_t len = strlen(name); + if (last_len < len && + !strncmp(name, last_symlink, last_len) && + name[last_len] == '/') + return 1; + *last_symlink = '\0'; + } + + while (1) { + size_t len; + struct stat st; + + ep = strchr(sp, '/'); + if (!ep) + break; + len = ep - sp; + if (PATH_MAX <= dp + len - path + 2) + return 0; /* new name is longer than that??? */ + memcpy(dp, sp, len); + dp[len] = 0; + + if (lstat(path, &st)) + return 0; + if (S_ISLNK(st.st_mode)) { + if (last_symlink) + strcpy(last_symlink, path); + return 1; + } + + dp[len++] = '/'; + dp = dp + len; + sp = ep + 1; + } + return 0; +} |