summaryrefslogtreecommitdiff
path: root/junkcode/fork0@users.sf.net-pathexpand/pathexpand.c
blob: 4b8bcf0f192e36c326567a91967d2d434e1bcaff (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

/*
 * Returns the analog of "cd path" from a directory "cwd".
 * The root is defined as empty path (instead of "/")
 * An attempt to go past the root (with "..") leaves the path at root.
 * The cwd is not expanded.
 */
char *pathexpand(const char *cwd, const char *path)
{
	static const char SEP[] = "/";
	if (!*path) /* empty path -> "." (don't move) */
		path = ".";
	if (!*cwd || *SEP == *path) /* no cwd, or path begins with "/" */
		cwd = "";

	while (*cwd && *SEP == *cwd)
		++cwd;

	size_t len = strlen(cwd);
	char *out = malloc(len + 1 + strlen(path) + 1);
	char *p = strcpy(out, cwd) + len;

	for (; *path; ++path)
	{
		char *pl;
		if (p > out && p[-1] != *SEP)
			*p++ = *SEP;
		pl = p;
		while (*path && *SEP != *path)
			*p++ = *path++;
		*p = '\0';
		/* ..."//"... */
		if (p == pl)
			; /* just ignore */
		/* ..."/./"...  */
		else if ( p - pl == 1 && '.' == *pl )
			--p; /* just ignore */
		/* ..."/../"...  */
		else if ( p - pl == 2 && '.' == pl[0] && '.' == pl[1] )
		{
			/* drop the last element of the resulting path */
			if (pl > out && --pl > out)
				for (--pl; pl > out && *SEP != *pl; --pl)
					;
			p = pl > out ? ++pl: out;
		}
		/* ..."/path/"...  */
		else if (*path)
			*p++ = *path; /* just add the separator */

		if (!*path)
			break;
	}
	if (p > out+1 && *SEP == p[-1])
		--p;
	*p = '\0';
	return out;
}

#ifdef CHECK_PATHEXPAND
static void check(const char *cwd, const char *path, const char *good)
{
	static int n = 0;
	printf("%-2d: %10s$ cd %s", ++n, cwd, path);
	char *t = pathexpand(cwd, path);
	if ( strcmp(t, good) )
		printf(" ____________________failed(%s)\n", t);
	else
		printf(" \033[32m%s\033[0m\n", t);
	free(t);
}

int main(int argc, char **argv)
{
	/* 1 */ check("/onelevel", "aa", "onelevel/aa");
	/* 2 */ check("/", "..", "");
	/* 3 */ check("/", "../..", "");
	/* 4 */ check("/one", "aa/../bb", "one/bb");
	/* 5 */ check("/one/two", "aa//bb", "one/two/aa/bb");
	/* 6 */ check("", "/aa//bb", "aa/bb");
	/* 7 */ check("/one/two", "", "one/two");
	/* 8 */ check("/one/two", "aa/..bb/x/../cc/", "one/two/aa/..bb/cc");
	/* 9 */ check("/one/two", "aa/x/././cc////", "one/two/aa/x/cc");
	/* 10 */ check("/one/two", "../../../../aa", "aa");
	/* 11 */ check("one/", "../one/two", "one/two");
	/* 12 */ check("", "../../two", "two");
	/* 13 */ check("a/b/c", "../../two", "a/two");
	/* 14 */ check("a/b/", "../two", "a/two");
	/* 15 */ check("///", "../two", "two");
	return 0;
}
#endif