blob: b98d5eeb4a244ca32d360a70cca90c64ff4c0c75 [file] [log] [blame]
DRC2ff39b82011-07-28 08:38:59 +00001//
2// "$Id: filename_absolute.cxx 8146 2010-12-31 22:13:07Z matt $"
3//
4// Filename expansion routines for the Fast Light Tool Kit (FLTK).
5//
6// Copyright 1998-2010 by Bill Spitzak and others.
7//
8// This library is free software; you can redistribute it and/or
9// modify it under the terms of the GNU Library General Public
10// License as published by the Free Software Foundation; either
11// version 2 of the License, or (at your option) any later version.
12//
13// This library is distributed in the hope that it will be useful,
14// but WITHOUT ANY WARRANTY; without even the implied warranty of
15// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16// Library General Public License for more details.
17//
18// You should have received a copy of the GNU Library General Public
19// License along with this library; if not, write to the Free Software
20// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21// USA.
22//
23// Please report all bugs and problems on the following page:
24//
25// http://www.fltk.org/str.php
26//
27
28/* expand a file name by prepending current directory, deleting . and
29 .. (not really correct for symbolic links) between the prepended
30 current directory. Use $PWD if it exists.
31 Returns true if any changes were made.
32*/
33
34#include <FL/filename.H>
35#include <FL/fl_utf8.h>
36#include <stdlib.h>
37#include "flstring.h"
38#include <ctype.h>
39#if defined(WIN32) && !defined(__CYGWIN__)
40# include <direct.h>
41#else
42# include <unistd.h>
43#endif
44
45#if defined(WIN32) || defined(__EMX__) && !defined(__CYGWIN__)
46inline int isdirsep(char c) {return c=='/' || c=='\\';}
47#else
48#define isdirsep(c) ((c)=='/')
49#endif
50
51/** Makes a filename absolute from a relative filename.
52 \code
53 #include <FL/filename.H>
54 [..]
55 chdir("/var/tmp");
56 fl_filename_absolute(out, sizeof(out), "foo.txt"); // out="/var/tmp/foo.txt"
57 fl_filename_absolute(out, sizeof(out), "./foo.txt"); // out="/var/tmp/foo.txt"
58 fl_filename_absolute(out, sizeof(out), "../log/messages"); // out="/var/log/messages"
59 \endcode
60 \param[out] to resulting absolute filename
61 \param[in] tolen size of the absolute filename buffer
62 \param[in] from relative filename
63 \return 0 if no change, non zero otherwise
64 */
65int fl_filename_absolute(char *to, int tolen, const char *from) {
66 if (isdirsep(*from) || *from == '|'
67#if defined(WIN32) || defined(__EMX__) && !defined(__CYGWIN__)
68 || from[1]==':'
69#endif
70 ) {
71 strlcpy(to, from, tolen);
72 return 0;
73 }
74
75 char *a;
76 char *temp = new char[tolen];
77 const char *start = from;
78
79 a = fl_getcwd(temp, tolen);
80 if (!a) {
81 strlcpy(to, from, tolen);
82 delete[] temp;
83 return 0;
84 }
85#if defined(WIN32) || defined(__EMX__) && !defined(__CYGWIN__)
86 for (a = temp; *a; a++) if (*a=='\\') *a = '/'; // ha ha
87#else
88 a = temp+strlen(temp);
89#endif
90 if (isdirsep(*(a-1))) a--;
91 /* remove intermediate . and .. names: */
92 while (*start == '.') {
93 if (start[1]=='.' && isdirsep(start[2])) {
94 char *b;
95 for (b = a-1; b >= temp && !isdirsep(*b); b--);
96 if (b < temp) break;
97 a = b;
98 start += 3;
99 } else if (isdirsep(start[1])) {
100 start += 2;
101 } else if (!start[1]) {
102 start ++; // Skip lone "."
103 break;
104 } else
105 break;
106 }
107
108 *a++ = '/';
109 strlcpy(a,start,tolen - (a - temp));
110
111 strlcpy(to, temp, tolen);
112
113 delete[] temp;
114
115 return 1;
116}
117
118/** Makes a filename relative to the current working directory.
119 \code
120 #include <FL/filename.H>
121 [..]
122 chdir("/var/tmp/somedir"); // set cwd to /var/tmp/somedir
123 [..]
124 char out[FL_PATH_MAX];
125 fl_filename_relative(out, sizeof(out), "/var/tmp/somedir/foo.txt"); // out="foo.txt", return=1
126 fl_filename_relative(out, sizeof(out), "/var/tmp/foo.txt"); // out="../foo.txt", return=1
127 fl_filename_relative(out, sizeof(out), "foo.txt"); // out="foo.txt", return=0 (no change)
128 fl_filename_relative(out, sizeof(out), "./foo.txt"); // out="./foo.txt", return=0 (no change)
129 fl_filename_relative(out, sizeof(out), "../foo.txt"); // out="../foo.txt", return=0 (no change)
130 \endcode
131 \param[out] to resulting relative filename
132 \param[in] tolen size of the relative filename buffer
133 \param[in] from absolute filename
134 \return 0 if no change, non zero otherwise
135 */
136int // O - 0 if no change, 1 if changed
137fl_filename_relative(char *to, // O - Relative filename
138 int tolen, // I - Size of "to" buffer
139 const char *from) // I - Absolute filename
140{
141 char cwd_buf[FL_PATH_MAX]; // Current directory
142 // get the current directory and return if we can't
143 if (!fl_getcwd(cwd_buf, sizeof(cwd_buf))) {
144 strlcpy(to, from, tolen);
145 return 0;
146 }
147 return fl_filename_relative(to, tolen, from, cwd_buf);
148}
149
150
151/** Makes a filename relative to any other directory.
152 \param[out] to resulting relative filename
153 \param[in] tolen size of the relative filename buffer
154 \param[in] from absolute filename
155 \param[in] base relative to this absolute path
156 \return 0 if no change, non zero otherwise
157 */
158int // O - 0 if no change, 1 if changed
159fl_filename_relative(char *to, // O - Relative filename
160 int tolen, // I - Size of "to" buffer
161 const char *from, // I - Absolute filename
162 const char *base) { // I - Find path relative to this path
163
164 char *newslash; // Directory separator
165 const char *slash; // Directory separator
166 char *cwd = 0L, *cwd_buf = 0L;
167 if (base) cwd = cwd_buf = strdup(base);
168
169 // return if "from" is not an absolute path
170#if defined(WIN32) || defined(__EMX__)
171 if (from[0] == '\0' ||
172 (!isdirsep(*from) && !isalpha(*from) && from[1] != ':' &&
173 !isdirsep(from[2]))) {
174#else
175 if (from[0] == '\0' || !isdirsep(*from)) {
176#endif // WIN32 || __EMX__
177 strlcpy(to, from, tolen);
178 if (cwd_buf) free(cwd_buf);
179 return 0;
180 }
181
182 // return if "cwd" is not an absolute path
183#if defined(WIN32) || defined(__EMX__)
184 if (!cwd || cwd[0] == '\0' ||
185 (!isdirsep(*cwd) && !isalpha(*cwd) && cwd[1] != ':' &&
186 !isdirsep(cwd[2]))) {
187#else
188 if (!cwd || cwd[0] == '\0' || !isdirsep(*cwd)) {
189#endif // WIN32 || __EMX__
190 strlcpy(to, from, tolen);
191 if (cwd_buf) free(cwd_buf);
192 return 0;
193 }
194
195#if defined(WIN32) || defined(__EMX__)
196 // convert all backslashes into forward slashes
197 for (newslash = strchr(cwd, '\\'); newslash; newslash = strchr(newslash + 1, '\\'))
198 *newslash = '/';
199
200 // test for the exact same string and return "." if so
201 if (!strcasecmp(from, cwd)) {
202 strlcpy(to, ".", tolen);
203 free(cwd_buf);
204 return (1);
205 }
206
207 // test for the same drive. Return the absolute path if not
208 if (tolower(*from & 255) != tolower(*cwd & 255)) {
209 // Not the same drive...
210 strlcpy(to, from, tolen);
211 free(cwd_buf);
212 return 0;
213 }
214
215 // compare the path name without the drive prefix
216 from += 2; cwd += 2;
217#else
218 // test for the exact same string and return "." if so
219 if (!strcmp(from, cwd)) {
220 strlcpy(to, ".", tolen);
221 free(cwd_buf);
222 return (1);
223 }
224#endif // WIN32 || __EMX__
225
226 // compare both path names until we find a difference
227 for (slash = from, newslash = cwd;
228 *slash != '\0' && *newslash != '\0';
229 slash ++, newslash ++)
230 if (isdirsep(*slash) && isdirsep(*newslash)) continue;
231#if defined(WIN32) || defined(__EMX__) || defined(__APPLE__)
232 else if (tolower(*slash & 255) != tolower(*newslash & 255)) break;
233#else
234 else if (*slash != *newslash) break;
235#endif // WIN32 || __EMX__ || __APPLE__
236
237 // skip over trailing slashes
238 if ( *newslash == '\0' && *slash != '\0' && !isdirsep(*slash)
239 &&(newslash==cwd || !isdirsep(newslash[-1])) )
240 newslash--;
241
242 // now go back to the first character of the first differing paths segment
243 while (!isdirsep(*slash) && slash > from) slash --;
244 if (isdirsep(*slash)) slash ++;
245
246 // do the same for the current dir
247 if (isdirsep(*newslash)) newslash --;
248 if (*newslash != '\0')
249 while (!isdirsep(*newslash) && newslash > cwd) newslash --;
250
251 // prepare the destination buffer
252 to[0] = '\0';
253 to[tolen - 1] = '\0';
254
255 // now add a "previous dir" sequence for every following slash in the cwd
256 while (*newslash != '\0') {
257 if (isdirsep(*newslash)) strlcat(to, "../", tolen);
258
259 newslash ++;
260 }
261
262 // finally add the differing path from "from"
263 strlcat(to, slash, tolen);
264
265 free(cwd_buf);
266 return 1;
267}
268
269
270//
271// End of "$Id: filename_absolute.cxx 8146 2010-12-31 22:13:07Z matt $".
272//