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