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