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