1 /*
2  *
3  *  Copyright 2008 Yura Siamashka <yurand2@gmail.com>
4  *
5  *  This program is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation, either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include <string.h>
20 #include <glib.h>
21 
22 #include <geanyplugin.h>
23 #include "utils.h"
24 
25 extern GeanyData *geany_data;
26 
27 
28 /* Normalize a pathname. This collapses redundant separators and up-level references so that A//B, A/./B
29  * and A/foo/../B all become A/B. It does not normalize the case. On Windows, it converts forward
30  * slashes to backward slashes. It should be understood that this may change the meaning of the
31  * path if it contains symbolic links!
32  */
33 gchar *
normpath(const gchar * filename)34 normpath(const gchar * filename)
35 {
36 	gchar **v;
37 	gchar **p;
38 	gchar **out;
39 	gchar **pout;
40 	gchar *ret;
41 
42 	if (!filename || strlen(filename) == 0)
43 		return g_strdup(".");
44 	v = g_strsplit_set(filename, "/\\", -1);
45 	if (!g_strv_length(v))
46 		return g_strdup(".");
47 
48 	out = g_malloc0(sizeof(gchar *) * (g_strv_length(v) + 2));
49 	pout = out;
50 
51 	if (filename[0] == '.' && strcmp(v[0], ".") == 0)
52 	{
53 		*pout = g_strdup(".");
54 		pout++;
55 	}
56 	else if (filename[0] == '/')
57 	{
58 		*pout = g_strdup("/");
59 		pout++;
60 	}
61 
62 	for (p = v; *p; p++)
63 	{
64 		if (strcmp(*p, ".") == 0 || strcmp(*p, "") == 0)
65 		{
66 			continue;
67 		}
68 		else if (strcmp(*p, "..") == 0)
69 		{
70 			if (pout != out)
71 			{
72 				pout--;
73 				if (strcmp(*pout, "..") != 0)
74 				{
75 					g_free(*pout);
76 					*pout = NULL;
77 					continue;
78 				}
79 				pout++;
80 			}
81 		}
82 		*pout++ = g_strdup(*p);
83 	}
84 
85 	ret = g_build_filenamev(out);
86 
87 	g_strfreev(out);
88 	g_strfreev(v);
89 	return ret;
90 }
91 
92 gchar *
get_full_path(const gchar * location,const gchar * path)93 get_full_path(const gchar * location, const gchar * path)
94 {
95 	gchar *dir;
96 
97 	dir = g_path_get_dirname(location);
98 	setptr(dir, g_build_filename(dir, path, NULL));
99 	setptr(dir, normpath(dir));
100 	return dir;
101 }
102 
103 gchar *
get_relative_path(const gchar * location,const gchar * path)104 get_relative_path(const gchar * location, const gchar * path)
105 {
106 	gchar *dir;
107 	gchar *pth;
108 	gchar *ret = NULL;
109 
110 	gint plen;
111 	gint dlen;
112 
113 	if (!g_path_is_absolute(path))
114 	{
115 		return g_strdup(path);
116 	}
117 
118 	dir = normpath(location);
119 	pth = normpath(path);
120 
121 	plen = strlen(pth);
122 	dlen = strlen(dir);
123 
124 	if (strstr(pth, dir) == pth)
125 	{
126 		if (plen > dlen)
127 		{
128 			ret = g_strdup(path + strlen(dir) + 1);
129 		}
130 		else if (plen == dlen)
131 		{
132 			ret = g_strdup(".");
133 		}
134 	}
135 	g_free(dir);
136 	g_free(pth);
137 	return ret;
138 }
139 
140 
141 #ifdef UNITTESTS
142 #include <check.h>
143 
START_TEST(test_get_relative_path)144 START_TEST(test_get_relative_path)
145 {
146 	gchar *path;
147 	path = get_relative_path("/a/b", "/a/b/c");
148 	fail_unless(strcmp(path, "c") == 0, "expected: \"c\", get \"%s\"\n", path);
149 	g_free(path);
150 
151 	path = get_relative_path("/a/b", "/a/b");
152 	fail_unless(strcmp(path, ".") == 0, "expected: \".\", get \"%s\"\n", path);
153 	g_free(path);
154 }
155 
156 END_TEST;
157 
158 
159 TCase *
utils_test_case_create(void)160 utils_test_case_create(void)
161 {
162 	TCase *tc_utils = tcase_create("utils");
163 	tcase_add_test(tc_utils, test_get_relative_path);
164 	return tc_utils;
165 }
166 
167 
168 #endif
169