summaryrefslogtreecommitdiff
path: root/src/util.c
diff options
context:
space:
mode:
authorVicent Marti <tanoku@gmail.com>2011-02-05 12:42:41 +0200
committerVicent Marti <tanoku@gmail.com>2011-02-05 12:42:41 +0200
commitf725931b4865317b58c1f1600724cb36e586c332 (patch)
tree571497dd0b831f683ffa6326014db2549e5a9a21 /src/util.c
parentc836c332f17ff2da8bdf6d18fb3d59eac2586ca9 (diff)
downloadlibgit2-f725931b4865317b58c1f1600724cb36e586c332.tar.gz
Fix directory/path manipulation methods
The `dirname` and `dirbase` methods have been replaced with the Android implementation, which is actually compilant to some kind of standard. A new method `topdir` has been added, which returns the topmost directory in a path. These changes fix issue #49: `gitfo_prettify_dir_path` converts "./.git/" to ".git/", so the code at src/repository.c:190 goes out of bounds when trying to find the topmost directory. The new `git__topdir` method handles this gracefully, and the fixed `git__dirname` now returns the proper value for the repository's working dir. E.g. /repo/.git/ ==> working dir '/repo/' .git/ ==> working dir '.' Signed-off-by: Vicent Marti <tanoku@gmail.com>
Diffstat (limited to 'src/util.c')
-rw-r--r--src/util.c204
1 files changed, 166 insertions, 38 deletions
diff --git a/src/util.c b/src/util.c
index ef97f68a4..d9d77eccf 100644
--- a/src/util.c
+++ b/src/util.c
@@ -36,65 +36,193 @@ int git__suffixcmp(const char *str, const char *suffix)
return strcmp(str + (a - b), suffix);
}
-int git__dirname(char *dir, size_t n, char *path)
+/*
+ * Based on the Android implementation, BSD licensed.
+ * Check http://android.git.kernel.org/
+ */
+int git__basename_r(char *buffer, size_t bufflen, const char *path)
{
- char *s;
- size_t len;
+ const char *endp, *startp;
+ int len, result;
+
+ /* Empty or NULL string gets treated as "." */
+ if (path == NULL || *path == '\0') {
+ startp = ".";
+ len = 1;
+ goto Exit;
+ }
+
+ /* Strip trailing slashes */
+ endp = path + strlen(path) - 1;
+ while (endp > path && *endp == '/')
+ endp--;
+
+ /* All slashes becomes "/" */
+ if (endp == path && *endp == '/') {
+ startp = "/";
+ len = 1;
+ goto Exit;
+ }
+
+ /* Find the start of the base */
+ startp = endp;
+ while (startp > path && *(startp - 1) != '/')
+ startp--;
- assert(dir && n > 1);
+ len = endp - startp +1;
- if (!path || !*path || (s = strrchr(path, '/')) == NULL) {
- strcpy(dir, ".");
- return 1;
+Exit:
+ result = len;
+ if (buffer == NULL) {
+ return result;
+ }
+ if (len > (int)bufflen-1) {
+ len = (int)bufflen-1;
+ result = GIT_ENOMEM;
}
- if (s == path) { /* "/[aaa]" */
- strcpy(dir, "/");
- return 1;
+ if (len >= 0) {
+ memcpy(buffer, startp, len);
+ buffer[len] = 0;
}
+ return result;
+}
- if ((len = s - path) >= n)
- return GIT_ERROR;
+/*
+ * Based on the Android implementation, BSD licensed.
+ * Check http://android.git.kernel.org/
+ */
+int git__dirname_r(char *buffer, size_t bufflen, const char *path)
+{
+ const char *endp;
+ int result, len;
+
+ /* Empty or NULL string gets treated as "." */
+ if (path == NULL || *path == '\0') {
+ path = ".";
+ len = 1;
+ goto Exit;
+ }
+
+ /* Strip trailing slashes */
+ endp = path + strlen(path) - 1;
+ while (endp > path && *endp == '/')
+ endp--;
+
+ /* Find the start of the dir */
+ while (endp > path && *endp != '/')
+ endp--;
+
+ /* Either the dir is "/" or there are no slashes */
+ if (endp == path) {
+ path = (*endp == '/') ? "/" : ".";
+ len = 1;
+ goto Exit;
+ }
+
+ do {
+ endp--;
+ } while (endp > path && *endp == '/');
+
+ len = endp - path +1;
+
+Exit:
+ result = len;
+ if (len+1 > GIT_PATH_MAX) {
+ return GIT_ENOMEM;
+ }
+ if (buffer == NULL)
+ return result;
+
+ if (len > (int)bufflen-1) {
+ len = (int)bufflen-1;
+ result = GIT_ENOMEM;
+ }
+
+ if (len >= 0) {
+ memcpy(buffer, path, len);
+ buffer[len] = 0;
+ }
+ return result;
+}
+
+
+char *git__dirname(const char *path)
+{
+ char *dname = NULL;
+ int len;
+
+ len = (path ? strlen(path) : 0) + 2;
+ dname = (char *)git__malloc(len);
+ if (dname == NULL)
+ return NULL;
- memcpy(dir, path, len);
- dir[len] = '\0';
+ if (git__dirname_r(dname, len, path) < GIT_SUCCESS) {
+ free(dname);
+ return NULL;
+ }
- return len;
+ return dname;
}
-int git__basename(char *base, size_t n, char *path)
+char *git__basename(const char *path)
{
- char *s;
- size_t len;
+ char *bname = NULL;
+ int len;
- assert(base && n > 1);
+ len = (path ? strlen(path) : 0) + 2;
+ bname = (char *)git__malloc(len);
+ if (bname == NULL)
+ return NULL;
- if (!path || !*path) {
- strcpy(base, ".");
- return 1;
+ if (git__basename_r(bname, len, path) < GIT_SUCCESS) {
+ free(bname);
+ return NULL;
}
+
+ return bname;
+}
+
+
+const char *git__topdir(const char *path)
+{
+ size_t len;
+ int i;
+
+ assert(path);
len = strlen(path);
- if ((s = strrchr(path, '/')) == NULL) {
- if (len >= n)
- return GIT_ERROR;
- strcpy(base, path);
- return len;
- }
+ if (!len || path[len - 1] != '/')
+ return NULL;
- if (s == path && len == 1) { /* "/" */
- strcpy(base, "/");
- return 1;
- }
+ for (i = len - 2; i >= 0; --i)
+ if (path[i] == '/')
+ break;
- len -= s - path;
- if (len >= n)
- return GIT_ERROR;
+ return &path[i + 1];
+}
+
+static char *strtok_raw(char *output, char *src, char *delimit, int keep)
+{
+ while (*src && strchr(delimit, *src) == NULL)
+ *output++ = *src++;
- memcpy(base, s+1, len);
- base[len] = '\0';
+ *output = 0;
+
+ if (keep)
+ return src;
+ else
+ return *src ? src+1 : src;
+}
- return len;
+char *git__strtok(char *output, char *src, char *delimit)
+{
+ return strtok_raw(output, src, delimit, 0);
+}
+
+char *git__strtok_keep(char *output, char *src, char *delimit)
+{
+ return strtok_raw(output, src, delimit, 1);
}
void git__hexdump(const char *buffer, size_t len)