From 29495ab53bebbdfde86dc9488acfe236d3669e57 Mon Sep 17 00:00:00 2001
From: Richard Nyberg <rnyberg@murmeldjur.se>
Date: Sat, 14 Oct 2006 21:29:25 +0000
Subject: [PATCH] Add and use the function make_abs_path. realpath didn't fit
 my needs. make_abs_path doesn't need the directories to actually exist in the
 file system.

---
 cli/add.c   |   4 +-
 misc/subr.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 misc/subr.h |   1 +
 3 files changed, 107 insertions(+), 2 deletions(-)

diff --git a/cli/add.c b/cli/add.c
index 585529b..e88a0b0 100644
--- a/cli/add.c
+++ b/cli/add.c
@@ -89,8 +89,8 @@ cmd_add(int argc, char **argv)
         buf_write(&iob, td, tdlen);
     }
     buf_swrite(&iob, "\0");
-    if (realpath(iob.buf, dpath) == NULL)
-        err(1, "realpath '%s'", dpath);
+    if ((errno = make_abs_path(iob.buf, dpath)) != 0)
+        err(1, "make_abs_path '%s'", dpath);
     code = btpd_add(ipc, mi, mi_size, dpath, name);
     if (code == 0 && start) {
         struct ipc_torrent tspec;
diff --git a/misc/subr.c b/misc/subr.c
index bc11d16..70a04af 100644
--- a/misc/subr.c
+++ b/misc/subr.c
@@ -276,3 +276,107 @@ find_btpd_dir(void)
         asprintf(&res, "%s/.btpd", home);
     return res;
 }
+
+int
+make_abs_path(const char *in, char *out)
+{
+    int ii = 0, oi = 0, lastsep = 0;
+    switch (in[0]) {
+    case '\0':
+        return EINVAL;
+    case '/':
+        if (strlen(in) >= PATH_MAX)
+            return ENAMETOOLONG;
+        out[0] = '/';
+        oi++;
+        ii++;
+        break;
+    default:
+        if (getcwd(out, PATH_MAX) == NULL)
+            return errno;
+        oi = strlen(out);
+        if (oi + strlen(in) + 1 >= PATH_MAX)
+            return ENAMETOOLONG;
+        out[oi] = '/';
+        lastsep = oi;
+        oi++;
+        break;
+    }
+after_slash:
+    while (in[ii] == '/')
+        ii++;
+    switch(in[ii]) {
+    case '\0':
+        goto end;
+    case '.':
+        ii++;
+        goto one_dot;
+    default:
+        goto normal;
+    }
+one_dot:
+    switch (in[ii]) {
+    case '\0':
+        goto end;
+    case '/':
+        ii++;
+        goto after_slash;
+    case '.':
+        ii++;
+        goto two_dot;
+    default:
+        out[oi] = '.';
+        oi++;
+        goto normal;
+    }
+two_dot:
+    switch (in[ii]) {
+    case '\0':
+        if (lastsep == 0)
+            oi = 1;
+        else {
+            oi = lastsep;
+            while (out[oi - 1] != '/')
+                oi--;
+            lastsep = oi - 1;
+        }
+        goto end;
+    case '/':
+        if (lastsep == 0)
+            oi = 1;
+        else {
+            oi = lastsep;
+            while (out[oi - 1] != '/')
+                oi--;
+            lastsep = oi - 1;
+        }
+        ii++;
+        goto after_slash;
+    default:
+        out[oi] = '.';
+        out[oi + 1] = '.';
+        oi += 2;
+        goto normal;
+    }
+normal:
+    switch (in[ii]) {
+    case '\0':
+        goto end;
+    case '/':
+        out[oi] = '/';
+        lastsep = oi;
+        oi++;
+        ii++;
+        goto after_slash;
+    default:
+        out[oi] = in[ii];
+        oi++;
+        ii++;
+        goto normal;
+    }
+end:
+    if (oi == lastsep + 1 && lastsep != 0)
+        oi = lastsep;
+    out[oi] = '\0';
+    return 0;
+}
diff --git a/misc/subr.h b/misc/subr.h
index 8ef962c..fd9e002 100644
--- a/misc/subr.h
+++ b/misc/subr.h
@@ -36,5 +36,6 @@ int write_fully(int fd, const void *buf, size_t len);
 void *read_file(const char *path, void *buf, size_t *size);
 
 char *find_btpd_dir(void);
+int make_abs_path(const char *in, char *out);
 
 #endif