2 // "$Id: filename_absolute.cxx 8146 2010-12-31 22:13:07Z matt $"
4 // Filename expansion routines for the Fast Light Tool Kit (FLTK).
6 // Copyright 1998-2010 by Bill Spitzak and others.
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.
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.
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
23 // Please report all bugs and problems on the following page:
25 // http://www.fltk.org/str.php
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.
34 #include <FL/filename.H>
35 #include <FL/fl_utf8.h>
39 #if defined(WIN32) && !defined(__CYGWIN__)
45 #if defined(WIN32) || defined(__EMX__) && !defined(__CYGWIN__)
46 inline int isdirsep(char c
) {return c
=='/' || c
=='\\';}
48 #define isdirsep(c) ((c)=='/')
51 /** Makes a filename absolute from a relative filename.
53 #include <FL/filename.H>
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"
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
65 int fl_filename_absolute(char *to
, int tolen
, const char *from
) {
66 if (isdirsep(*from
) || *from
== '|'
67 #if defined(WIN32) || defined(__EMX__) && !defined(__CYGWIN__)
71 strlcpy(to
, from
, tolen
);
76 char *temp
= new char[tolen
];
77 const char *start
= from
;
79 a
= fl_getcwd(temp
, tolen
);
81 strlcpy(to
, from
, tolen
);
85 #if defined(WIN32) || defined(__EMX__) && !defined(__CYGWIN__)
86 for (a
= temp
; *a
; a
++) if (*a
=='\\') *a
= '/'; // ha ha
88 a
= temp
+strlen(temp
);
90 if (isdirsep(*(a
-1))) a
--;
91 /* remove intermediate . and .. names: */
92 while (*start
== '.') {
93 if (start
[1]=='.' && isdirsep(start
[2])) {
95 for (b
= a
-1; b
>= temp
&& !isdirsep(*b
); b
--);
99 } else if (isdirsep(start
[1])) {
101 } else if (!start
[1]) {
102 start
++; // Skip lone "."
109 strlcpy(a
,start
,tolen
- (a
- temp
));
111 strlcpy(to
, temp
, tolen
);
118 /** Makes a filename relative to the current working directory.
120 #include <FL/filename.H>
122 chdir("/var/tmp/somedir"); // set cwd to /var/tmp/somedir
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)
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
136 int // O - 0 if no change, 1 if changed
137 fl_filename_relative(char *to
, // O - Relative filename
138 int tolen
, // I - Size of "to" buffer
139 const char *from
) // I - Absolute filename
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
);
147 return fl_filename_relative(to
, tolen
, from
, cwd_buf
);
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
158 int // O - 0 if no change, 1 if changed
159 fl_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
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
);
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]))) {
175 if (from
[0] == '\0' || !isdirsep(*from
)) {
176 #endif // WIN32 || __EMX__
177 strlcpy(to
, from
, tolen
);
178 if (cwd_buf
) free(cwd_buf
);
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]))) {
188 if (!cwd
|| cwd
[0] == '\0' || !isdirsep(*cwd
)) {
189 #endif // WIN32 || __EMX__
190 strlcpy(to
, from
, tolen
);
191 if (cwd_buf
) free(cwd_buf
);
195 #if defined(WIN32) || defined(__EMX__)
196 // convert all backslashes into forward slashes
197 for (newslash
= strchr(cwd
, '\\'); newslash
; newslash
= strchr(newslash
+ 1, '\\'))
200 // test for the exact same string and return "." if so
201 if (!strcasecmp(from
, cwd
)) {
202 strlcpy(to
, ".", tolen
);
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
);
215 // compare the path name without the drive prefix
218 // test for the exact same string and return "." if so
219 if (!strcmp(from
, cwd
)) {
220 strlcpy(to
, ".", tolen
);
224 #endif // WIN32 || __EMX__
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;
234 else if (*slash
!= *newslash
) break;
235 #endif // WIN32 || __EMX__ || __APPLE__
237 // skip over trailing slashes
238 if ( *newslash
== '\0' && *slash
!= '\0' && !isdirsep(*slash
)
239 &&(newslash
==cwd
|| !isdirsep(newslash
[-1])) )
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
++;
246 // do the same for the current dir
247 if (isdirsep(*newslash
)) newslash
--;
248 if (*newslash
!= '\0')
249 while (!isdirsep(*newslash
) && newslash
> cwd
) newslash
--;
251 // prepare the destination buffer
253 to
[tolen
- 1] = '\0';
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
);
262 // finally add the differing path from "from"
263 strlcat(to
, slash
, tolen
);
271 // End of "$Id: filename_absolute.cxx 8146 2010-12-31 22:13:07Z matt $".