1 /*
2 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 * Copyright (C) 2006 - INRIA - Pierre MARECHAL
4 * Copyright (C) 2011 - Digiteo - Cedric DELAMARRE
5 * Copyright (C) 2011 - DIGITEO - Allan CORNET
6 *
7 * Copyright (C) 2012 - 2016 - Scilab Enterprises
8 *
9 * This file is hereby licensed under the terms of the GNU GPL v2.0,
10 * pursuant to article 5.3.4 of the CeCILL v.2.1.
11 * This file was originally licensed under the terms of the CeCILL v2.1,
12 * and continues to be available under such terms.
13 * For more information, see the COPYING file which you should have received
14 * along with this program.
15 *
16 */
17 #include <ctype.h>
18 #include <string.h>
19 #include "machine.h" /* DIR_SEPARATOR */
20 #include "getrelativefilename.h"
21 #include "sci_malloc.h"
22 #include "PATH_MAX.h"
23 #include "strsubst.h"
24 /* ================================================================================== */
25 static char *normalizeFileSeparator(const char *path);
26 static wchar_t *normalizeFileSeparatorW(const wchar_t *path);
27 /* ================================================================================== */
28 // getrelativefilename
29 //
30 // Given the absolute current directory and an absolute file name, returns a relative file name.
31 // For example, if the current directory is C:\foo\bar and the filename C:\foo\whee\text.txt is given,
32 // GetRelativeFilename will return ..\whee\text.txt.
33 /* ================================================================================== */
getrelativefilename(char * currentDirectory,char * absoluteFilename)34 char* getrelativefilename(char *currentDirectory, char *absoluteFilename)
35 {
36 int afMarker = 0, rfMarker = 0;
37 int cdLen = 0, afLen = 0;
38 int i = 0;
39 int levels = 0;
40 char *relativeFilename = (char*)MALLOC(PATH_MAX * sizeof(char));
41 char *_currentDirectory = normalizeFileSeparator(currentDirectory);
42 char *_absoluteFilename = normalizeFileSeparator(absoluteFilename);
43
44 cdLen = (int)strlen(_currentDirectory);
45 afLen = (int)strlen(_absoluteFilename);
46
47 // make sure the names are not too short
48 if ( cdLen < ABSOLUTE_NAME_START + 1 || afLen < ABSOLUTE_NAME_START + 1)
49 {
50 // fix bug 2181
51 strcpy(relativeFilename, _absoluteFilename);
52 FREE(_currentDirectory);
53 FREE(_absoluteFilename);
54 return relativeFilename;
55 }
56
57 // Handle DOS names that are on different drives:
58 if (tolower(_currentDirectory[0]) != tolower(_absoluteFilename[0]))
59 {
60 // not on the same drive, so only absolute filename will do
61 strcpy(relativeFilename, _absoluteFilename);
62 FREE(_currentDirectory);
63 FREE(_absoluteFilename);
64 return relativeFilename;
65 }
66
67 // they are on the same drive, find out how much of the current directory
68 // is in the absolute filename
69 i = ABSOLUTE_NAME_START;
70
71 #if defined(_MSC_VER)
72 while (i < afLen && i < cdLen && tolower(_currentDirectory[i]) == tolower(_absoluteFilename[i]) )
73 {
74 i++;
75 }
76 #else
77 while (i < afLen && i < cdLen && _currentDirectory[i] == _absoluteFilename[i])
78 {
79 i++;
80 }
81 #endif
82
83 if (i == cdLen && (_absoluteFilename[i] == DIR_SEPARATOR[0] || _absoluteFilename[i - 1] == DIR_SEPARATOR[0]))
84 {
85 // the whole current directory name is in the file name,
86 // so we just trim off the current directory name to get the
87 // current file name.
88 if (_absoluteFilename[i] == DIR_SEPARATOR[0])
89 {
90 // a directory name might have a trailing slash but a relative
91 // file name should not have a leading one...
92 i++;
93 }
94
95 strcpy(relativeFilename, &_absoluteFilename[i]);
96 FREE(_currentDirectory);
97 FREE(_absoluteFilename);
98 return relativeFilename;
99 }
100
101 // The file is not in a child directory of the current directory, so we
102 // need to step back the appropriate number of parent directories by
103 // using "..\"s. First find out how many levels deeper we are than the
104 // common directory
105 afMarker = i;
106 levels = 1;
107
108 // count the number of directory levels we have to go up to get to the
109 // common directory
110 while (i < cdLen)
111 {
112 i++;
113 if (_currentDirectory[i] == DIR_SEPARATOR[0])
114 {
115 // make sure it's not a trailing slash
116 i++;
117 if (_currentDirectory[i] != '\0')
118 {
119 levels++;
120 }
121 }
122 }
123
124 // move the absolute filename marker back to the start of the directory name
125 // that it has stopped in.
126 while (afMarker > 0 && _absoluteFilename[afMarker - 1] != DIR_SEPARATOR[0])
127 {
128 afMarker--;
129 }
130
131 // check that the result will not be too long
132 if (levels * 3 + afLen - afMarker > PATH_MAX)
133 {
134 FREE(relativeFilename);
135 FREE(_currentDirectory);
136 FREE(_absoluteFilename);
137 return NULL;
138 }
139
140 // add the appropriate number of "..\"s.
141 rfMarker = 0;
142 for (i = 0; i < levels; i++)
143 {
144 relativeFilename[rfMarker++] = '.';
145 relativeFilename[rfMarker++] = '.';
146 relativeFilename[rfMarker++] = DIR_SEPARATOR[0];
147 }
148
149 // copy the rest of the filename into the result string
150 strcpy(&relativeFilename[rfMarker], &_absoluteFilename[afMarker]);
151
152 FREE(_currentDirectory);
153 FREE(_absoluteFilename);
154 return relativeFilename;
155 }
156
getrelativefilenameW(wchar_t * currentDirectory,wchar_t * absoluteFilename)157 wchar_t* getrelativefilenameW(wchar_t *currentDirectory, wchar_t *absoluteFilename)
158 {
159 // declarations - put here so this should work in a C compiler
160 int afMarker = 0, rfMarker = 0;
161 int cdLen = 0, afLen = 0;
162 int i = 0;
163 int levels = 0;
164 wchar_t *relativeFilename = (wchar_t*)MALLOC(PATH_MAX * sizeof(wchar_t));
165 wchar_t *_currentDirectory = normalizeFileSeparatorW(currentDirectory);
166 wchar_t *_absoluteFilename = normalizeFileSeparatorW(absoluteFilename);
167
168 cdLen = (int)wcslen(_currentDirectory);
169 afLen = (int)wcslen(_absoluteFilename);
170
171 // make sure the names are not too short
172 if ( cdLen < ABSOLUTE_NAME_START + 1 || afLen < ABSOLUTE_NAME_START + 1)
173 {
174 // fix bug 2181
175 wcscpy(relativeFilename, _absoluteFilename);
176 return relativeFilename;
177 }
178
179 // Handle DOS names that are on different drives:
180 if (tolower(_currentDirectory[0]) != tolower(_absoluteFilename[0]))
181 {
182 // not on the same drive, so only absolute filename will do
183 wcscpy(relativeFilename, _absoluteFilename);
184 return relativeFilename;
185 }
186
187 // they are on the same drive, find out how much of the current directory
188 // is in the absolute filename
189 i = ABSOLUTE_NAME_START;
190
191 #if defined(_MSC_VER)
192 while (i < afLen && i < cdLen && tolower(_currentDirectory[i]) == tolower(_absoluteFilename[i]) )
193 {
194 i++;
195 }
196 #else
197 while (i < afLen && i < cdLen && _currentDirectory[i] == _absoluteFilename[i])
198 {
199 i++;
200 }
201 #endif
202
203 if (i == cdLen && (_absoluteFilename[i] == DIR_SEPARATOR[0] || _absoluteFilename[i - 1] == DIR_SEPARATOR[0]))
204 {
205 // the whole current directory name is in the file name,
206 // so we just trim off the current directory name to get the
207 // current file name.
208 if (_absoluteFilename[i] == DIR_SEPARATOR[0])
209 {
210 // a directory name might have a trailing slash but a relative
211 // file name should not have a leading one...
212 i++;
213 }
214
215 wcscpy(relativeFilename, &_absoluteFilename[i]);
216 return relativeFilename;
217 }
218
219 // The file is not in a child directory of the current directory, so we
220 // need to step back the appropriate number of parent directories by
221 // using "..\"s. First find out how many levels deeper we are than the
222 // common directory
223 afMarker = i;
224 levels = 1;
225
226 // count the number of directory levels we have to go up to get to the
227 // common directory
228 while (i < cdLen)
229 {
230 i++;
231 if (_currentDirectory[i] == DIR_SEPARATOR[0])
232 {
233 // make sure it's not a trailing slash
234 i++;
235 if (currentDirectory[i] != '\0')
236 {
237 levels++;
238 }
239 }
240 }
241
242 // move the absolute filename marker back to the start of the directory name
243 // that it has stopped in.
244 while (afMarker > 0 && _absoluteFilename[afMarker - 1] != DIR_SEPARATOR[0])
245 {
246 afMarker--;
247 }
248
249 // check that the result will not be too long
250 if (levels * 3 + afLen - afMarker > PATH_MAX)
251 {
252 FREE(relativeFilename);
253 return NULL;
254 }
255
256 // add the appropriate number of "..\"s.
257 rfMarker = 0;
258 for (i = 0; i < levels; i++)
259 {
260 relativeFilename[rfMarker++] = '.';
261 relativeFilename[rfMarker++] = '.';
262 relativeFilename[rfMarker++] = DIR_SEPARATOR[0];
263 }
264
265 // copy the rest of the filename into the result string
266 wcscpy(&relativeFilename[rfMarker], &_absoluteFilename[afMarker]);
267
268 return relativeFilename;
269 }
270
271 /* ================================================================================== */
normalizeFileSeparator(const char * path)272 char *normalizeFileSeparator(const char *path)
273 {
274 #define WINDOWS_FILESEPARATOR "\\"
275 #define OTHERS_FILESEPARATOR "/"
276
277 char *normalizedPath = NULL;
278 if (path)
279 {
280 #ifdef _MSC_VER
281 normalizedPath = strsub((char*)path, OTHERS_FILESEPARATOR, DIR_SEPARATOR);
282 #else
283 normalizedPath = strsub((char*)path, WINDOWS_FILESEPARATOR, DIR_SEPARATOR);
284 #endif
285 }
286 return normalizedPath;
287 }
288
normalizeFileSeparatorW(const wchar_t * path)289 wchar_t *normalizeFileSeparatorW(const wchar_t *path)
290 {
291 // TODO : Implement strsubW to have same behaviour.
292 return (wchar_t*)path;
293 }
294 /* ================================================================================== */
295
296