Use filename_extension() and filename_sans_extension().
[synfig.git] / ETL / trunk / ETL / _stringf.h
1 /* =========================================================================
2 ** Extended Template and Library
3 ** stringf Procedure Implementation
4 ** $Id$
5 **
6 ** Copyright (c) 2002 Robert B. Quattlebaum Jr.
7 ** Copyright (c) 2007 Chris Moore
8 **
9 ** This package is free software; you can redistribute it and/or
10 ** modify it under the terms of the GNU General Public License as
11 ** published by the Free Software Foundation; either version 2 of
12 ** the License, or (at your option) any later version.
13 **
14 ** This package is distributed in the hope that it will be useful,
15 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 ** General Public License for more details.
18 **
19 ** === N O T E S ===========================================================
20 **
21 ** This is an internal header file, included by other ETL headers.
22 ** You should not attempt to use it directly.
23 **
24 ** ========================================================================= */
25
26 /* === S T A R T =========================================================== */
27
28 #ifndef __ETL__STRINGF_H
29 #define __ETL__STRINGF_H
30
31 /* === H E A D E R S ======================================================= */
32
33 #include <string>
34 #include <cstdarg>
35 #include <cstdlib>
36
37 /* === M A C R O S ========================================================= */
38
39 #ifndef ETL_STRPRINTF_MAX_LENGTH
40 #define ETL_STRPRINTF_MAX_LENGTH        (800)
41 #endif
42
43 /* === T Y P E D E F S ===================================================== */
44
45 _ETL_BEGIN_CDECLS
46
47 #if defined(__APPLE__) || defined(__CYGWIN__) || defined(_WIN32)
48 #define ETL_NO_THROW
49 #else
50 #define ETL_NO_THROW throw()
51 #endif
52
53 #ifdef HAVE_VASPRINTF   // This is the preferred method
54  extern int vasprintf(char **,const char *,va_list)ETL_NO_THROW;
55 #else
56
57 # ifdef HAVE_VSNPRINTF  // This is the secondary method
58  extern int vsnprintf(char *,size_t,const char*,va_list)ETL_NO_THROW;
59 # endif
60
61 #endif
62
63 #ifdef HAVE_VSSCANF
64 extern int vsscanf(const char *,const char *,va_list)ETL_NO_THROW;
65 #else
66 #define ETL_NO_VSTRSCANF
67 #ifdef HAVE_SSCANF
68 extern int sscanf(const char *buf, const char *format, ...)ETL_NO_THROW;
69 #endif
70 #endif
71
72 #include <unistd.h>
73
74 _ETL_END_CDECLS
75
76 /* === C L A S S E S & S T R U C T S ======================================= */
77
78 _ETL_BEGIN_NAMESPACE
79
80 inline std::string
81 vstrprintf(const char *format, va_list args)
82 {
83 #ifdef HAVE_VASPRINTF   // This is the preferred method (and safest)
84         char *buffer;
85         std::string ret;
86         vasprintf(&buffer,format,args);
87         ret=buffer;
88         free(buffer);
89         return ret;
90 #else
91 #ifdef HAVE_VSNPRINTF   // This is the secondary method (Safe, but bulky)
92 #warning etl::vstrprintf() has a maximum size of ETL_STRPRINTF_MAX_LENGTH in this configuration.
93 #ifdef ETL_THREAD_SAFE
94         char buffer[ETL_STRPRINTF_MAX_LENGTH];
95 #else
96         static char buffer[ETL_STRPRINTF_MAX_LENGTH];
97 #endif
98         vsnprintf(buffer,sizeof(buffer),format,args);
99         return buffer;
100 #else                                   // This is the worst method (UNSAFE, but "works")
101 #warning Potential for Buffer-overflow bug using vsprintf
102 #define ETL_UNSAFE_STRPRINTF    (true)
103 // Here, we are doubling the size of the buffer to make this case
104 // slightly more safe.
105 #ifdef ETL_THREAD_SAFE
106         char buffer[ETL_STRPRINTF_MAX_LENGTH*2];
107 #else
108         static char buffer[ETL_STRPRINTF_MAX_LENGTH*2];
109 #endif
110         vsprintf(buffer,format,args);
111         return buffer;
112 #endif
113 #endif
114 }
115
116 inline std::string
117 strprintf(const char *format, ...)
118 {
119         va_list args;
120         va_start(args,format);
121         return vstrprintf(format,args);
122 }
123
124 #ifndef ETL_NO_VSTRSCANF
125 inline int
126 vstrscanf(const std::string &data, const char*format, va_list args)
127 {
128     return vsscanf(data.c_str(),format,args);
129 }
130
131 inline int
132 strscanf(const std::string &data, const char*format, ...)
133 {
134         va_list args;
135         va_start(args,format);
136         return vstrscanf(data, format,args);
137 }
138 #else
139
140 #if defined (HAVE_SSCANF) && defined (__GNUC__)
141 #define strscanf(data,format,...) sscanf(data.c_str(),format,__VA_ARGS__)
142 #endif
143 #endif
144
145
146 #define stratof(X) (atof((X).c_str()))
147 #define stratoi(X) (atoi((X).c_str()))
148
149 inline std::string
150 basename(const std::string &str)
151 {
152         std::string::const_iterator iter;
153
154         if(str.size() == 1 && str[0] == ETL_DIRECTORY_SEPARATOR)
155                 return str;
156
157         if(str.end()[-1]==ETL_DIRECTORY_SEPARATOR)
158                 iter=str.end()-2;
159         else
160                 iter=str.end()-1;
161
162         for(;iter!=str.begin();iter--)
163                 if(*iter==ETL_DIRECTORY_SEPARATOR)
164                         break;
165
166         if (*iter==ETL_DIRECTORY_SEPARATOR)
167                 iter++;
168
169         if(str.end()[-1]==ETL_DIRECTORY_SEPARATOR)
170                 return std::string(iter,str.end()-1);
171
172         return std::string(iter,str.end());
173 }
174
175 inline std::string
176 dirname(const std::string &str)
177 {
178         std::string::const_iterator iter;
179
180         if(str.size() == 1 && str[0] == ETL_DIRECTORY_SEPARATOR)
181                 return str;
182
183         if(str.end()[-1]==ETL_DIRECTORY_SEPARATOR)
184                 iter=str.end()-2;
185         else
186                 iter=str.end()-1;
187
188         for(;iter!=str.begin();iter--)
189                 if(*iter==ETL_DIRECTORY_SEPARATOR)
190                         break;
191
192         if(iter==str.begin())
193            if (*iter==ETL_DIRECTORY_SEPARATOR)
194                    return "/";
195            else
196                    return ".";
197
198         return std::string(str.begin(),iter);
199 }
200
201 // filename_extension("/f.e/d.c") => ".c"
202 inline std::string
203 filename_extension(const std::string &str)
204 {
205         std::string base = basename(str);
206         std::string::size_type pos = base.find_last_of('.');
207         if (pos == std::string::npos) return std::string();
208         return base.substr(pos);
209 }
210
211 // filename_sans_extension("/f.e/d.c") => "/f.e/d"
212 inline std::string
213 filename_sans_extension(const std::string &str)
214 {
215         std::string base = basename(str);
216         std::string::size_type pos = base.find_last_of('.');
217         if (pos == std::string::npos) return str;
218         std::string dir = dirname(str);
219         if (dir == ".") return base.substr(0,pos);
220         return dir + ETL_DIRECTORY_SEPARATOR + base.substr(0,pos);
221 }
222
223 inline bool
224 is_absolute_path(const std::string &path)
225 {
226 #ifdef WIN32
227         if(path.size()>=3 && path[1]==':' && (path[2]=='\\' || path[2]=='/'))
228                 return true;
229 #endif
230         if(!path.empty() && path[0]==ETL_DIRECTORY_SEPARATOR)
231                 return true;
232         return false;
233 }
234
235 inline std::string
236 unix_to_local_path(const std::string &path)
237 {
238         std::string ret;
239         std::string::const_iterator iter;
240         for(iter=path.begin();iter!=path.end();iter++)
241                 switch(*iter)
242                 {
243                 case '/':
244                         ret+=ETL_DIRECTORY_SEPARATOR;
245                         break;
246                 case '~':
247                         ret+='~';
248                         break;
249                 default:
250                         ret+=*iter;
251                         break;
252                 }
253         return ret;
254 }
255
256 inline std::string
257 current_working_directory()
258 {
259         char dir[256];
260         std::string ret(getcwd(dir,sizeof(dir)));
261         return ret;
262 }
263
264 inline std::string
265 get_root_from_path(std::string path)
266 {
267         std::string ret;
268         std::string::const_iterator iter;
269
270         for(iter=path.begin();iter!=path.end();++iter)
271         {
272                 if(*iter==ETL_DIRECTORY_SEPARATOR)
273                         break;
274                 ret+=*iter;
275         }
276         //if(iter!=path.end())
277                 ret+=ETL_DIRECTORY_SEPARATOR;
278         return ret;
279 }
280
281 inline std::string
282 remove_root_from_path(std::string path)
283 {
284         while(!path.empty())
285         {
286                 if(path[0]==ETL_DIRECTORY_SEPARATOR)
287                 {
288                         path.erase(path.begin());
289                         return path;
290                 }
291                 path.erase(path.begin());
292         }
293         return path;
294 }
295
296 inline std::string
297 cleanup_path(std::string path)
298 {
299         std::string ret;
300
301         while(basename(path)=="."&&path.size()!=1)path=dirname(path);
302
303         while(!path.empty())
304         {
305                 std::string dir(get_root_from_path(path));
306                 if((dir=="../" || dir=="..\\") && ret.size())
307                 {
308                         ret=dirname(ret);
309                         if (*(ret.end()-1)!=ETL_DIRECTORY_SEPARATOR)
310                                 ret+=ETL_DIRECTORY_SEPARATOR;
311                 }
312                 else if((dir!="./" && dir!=".\\") && dir!=".")
313                         ret+=dir;
314                 path=remove_root_from_path(path);
315         }
316         if (ret.size()==0)ret+='.';
317
318         // Remove any trailing directory separators
319         if(ret.size() && ret[ret.size()-1]==ETL_DIRECTORY_SEPARATOR)
320                 ret.erase(ret.begin()+ret.size()-1);
321         return ret;
322 }
323
324 inline std::string
325 absolute_path(std::string path)
326 {
327         std::string ret(current_working_directory());
328
329         if(path.empty())
330                 return cleanup_path(ret);
331         if(is_absolute_path(path))
332                 return cleanup_path(path);
333         return cleanup_path(ret+ETL_DIRECTORY_SEPARATOR+path);
334 }
335
336 inline std::string
337 relative_path(std::string curr_path,std::string dest_path)
338 {
339         // If dest_path is already a relative path,
340         // then there is no need to do anything.
341         if(!is_absolute_path(dest_path))
342                 dest_path=absolute_path(dest_path);
343         else
344                 dest_path=cleanup_path(dest_path);
345
346         if(!is_absolute_path(curr_path))
347                 curr_path=absolute_path(curr_path);
348         else
349                 curr_path=cleanup_path(curr_path);
350
351 #ifdef WIN32
352         // If we are on windows and the dest path is on a different drive,
353         // then there is no way to make a relative path to it.
354         if(dest_path.size()>=3 && dest_path[1]==':' && dest_path[0]!=curr_path[0])
355                 return dest_path;
356 #endif
357
358         if(curr_path==dirname(dest_path))
359                 return basename(dest_path);
360
361         while(!dest_path.empty() && !curr_path.empty() && get_root_from_path(dest_path)==get_root_from_path(curr_path))
362         {
363                 dest_path=remove_root_from_path(dest_path);
364                 curr_path=remove_root_from_path(curr_path);
365         }
366
367         while(!curr_path.empty())
368         {
369                 dest_path=std::string("..")+ETL_DIRECTORY_SEPARATOR+dest_path;
370                 curr_path=remove_root_from_path(curr_path);
371         }
372
373         return dest_path;
374 }
375
376 _ETL_END_NAMESPACE
377
378 /* === E N D =============================================================== */
379
380 #endif