New junkcode.
[ccan] / junkcode / fork0@users.sf.net-pathexpand / pathexpand.c
1 #include <string.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4
5 /*
6  * Returns the analog of "cd path" from a directory "cwd".
7  * The root is defined as empty path (instead of "/")
8  * An attempt to go past the root (with "..") leaves the path at root.
9  * The cwd is not expanded.
10  */
11 char *pathexpand(const char *cwd, const char *path)
12 {
13         static const char SEP[] = "/";
14         if (!*path) /* empty path -> "." (don't move) */
15                 path = ".";
16         if (!*cwd || *SEP == *path) /* no cwd, or path begins with "/" */
17                 cwd = "";
18
19         while (*cwd && *SEP == *cwd)
20                 ++cwd;
21
22         size_t len = strlen(cwd);
23         char *out = malloc(len + 1 + strlen(path) + 1);
24         char *p = strcpy(out, cwd) + len;
25
26         for (; *path; ++path)
27         {
28                 char *pl;
29                 if (p > out && p[-1] != *SEP)
30                         *p++ = *SEP;
31                 pl = p;
32                 while (*path && *SEP != *path)
33                         *p++ = *path++;
34                 *p = '\0';
35                 /* ..."//"... */
36                 if (p == pl)
37                         ; /* just ignore */
38                 /* ..."/./"...  */
39                 else if ( p - pl == 1 && '.' == *pl )
40                         --p; /* just ignore */
41                 /* ..."/../"...  */
42                 else if ( p - pl == 2 && '.' == pl[0] && '.' == pl[1] )
43                 {
44                         /* drop the last element of the resulting path */
45                         if (pl > out && --pl > out)
46                                 for (--pl; pl > out && *SEP != *pl; --pl)
47                                         ;
48                         p = pl > out ? ++pl: out;
49                 }
50                 /* ..."/path/"...  */
51                 else if (*path)
52                         *p++ = *path; /* just add the separator */
53
54                 if (!*path)
55                         break;
56         }
57         if (p > out+1 && *SEP == p[-1])
58                 --p;
59         *p = '\0';
60         return out;
61 }
62
63 #ifdef CHECK_PATHEXPAND
64 static void check(const char *cwd, const char *path, const char *good)
65 {
66         static int n = 0;
67         printf("%-2d: %10s$ cd %s", ++n, cwd, path);
68         char *t = pathexpand(cwd, path);
69         if ( strcmp(t, good) )
70                 printf(" ____________________failed(%s)\n", t);
71         else
72                 printf(" \033[32m%s\033[0m\n", t);
73         free(t);
74 }
75
76 int main(int argc, char **argv)
77 {
78         /* 1 */ check("/onelevel", "aa", "onelevel/aa");
79         /* 2 */ check("/", "..", "");
80         /* 3 */ check("/", "../..", "");
81         /* 4 */ check("/one", "aa/../bb", "one/bb");
82         /* 5 */ check("/one/two", "aa//bb", "one/two/aa/bb");
83         /* 6 */ check("", "/aa//bb", "aa/bb");
84         /* 7 */ check("/one/two", "", "one/two");
85         /* 8 */ check("/one/two", "aa/..bb/x/../cc/", "one/two/aa/..bb/cc");
86         /* 9 */ check("/one/two", "aa/x/././cc////", "one/two/aa/x/cc");
87         /* 10 */ check("/one/two", "../../../../aa", "aa");
88         /* 11 */ check("one/", "../one/two", "one/two");
89         /* 12 */ check("", "../../two", "two");
90         /* 13 */ check("a/b/c", "../../two", "a/two");
91         /* 14 */ check("a/b/", "../two", "a/two");
92         /* 15 */ check("///", "../two", "two");
93         return 0;
94 }
95 #endif
96