Revert modifications for Fedora compatibility due to stability problems.
[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 #ifndef _GNU_SOURCE
34 #define _GNU_SOURCE
35 #endif
36
37 #include <string>
38 #include <cstdarg>
39 #include <cstdlib>
40 #include <cstdio>
41
42 /* === M A C R O S ========================================================= */
43
44 #ifndef ETL_STRPRINTF_MAX_LENGTH
45 #define ETL_STRPRINTF_MAX_LENGTH        (800)
46 #endif
47
48 #ifdef WIN32
49 #define POPEN_BINARY_READ_TYPE "rb"
50 #define POPEN_BINARY_WRITE_TYPE "wb"
51 #else
52 #define POPEN_BINARY_READ_TYPE "r"
53 #define POPEN_BINARY_WRITE_TYPE "w"
54 #endif
55
56 /* === T Y P E D E F S ===================================================== */
57
58 _ETL_BEGIN_CDECLS
59
60 #if defined(__APPLE__) || defined(__CYGWIN__) || defined(_WIN32)
61 #define ETL_NO_THROW
62 #else
63 #define ETL_NO_THROW throw()
64 #endif
65
66 // Prefer prototypes from glibc headers, since defining them ourselves
67 // works around glibc security mechanisms
68
69 #ifdef HAVE_VASPRINTF   // This is the preferred method
70  #ifndef __GLIBC__
71   extern int vasprintf(char **,const char *,va_list)ETL_NO_THROW;
72  #endif
73 #else
74
75 # ifdef HAVE_VSNPRINTF  // This is the secondary method
76  #ifndef __GLIBC__
77   extern int vsnprintf(char *,size_t,const char*,va_list)ETL_NO_THROW;
78  #endif
79 # endif
80
81 #endif
82
83 #ifdef HAVE_VSSCANF
84  #ifndef __GLIBC__
85   extern int vsscanf(const char *,const char *,va_list)ETL_NO_THROW;
86  #endif
87 #else
88 #define ETL_NO_VSTRSCANF
89 #ifdef HAVE_SSCANF
90  #ifndef __GLIBC__
91   extern int sscanf(const char *buf, const char *format, ...)ETL_NO_THROW;
92  #endif
93 #endif
94 #endif
95
96 #include <unistd.h>
97
98 _ETL_END_CDECLS
99
100 /* === C L A S S E S & S T R U C T S ======================================= */
101
102 _ETL_BEGIN_NAMESPACE
103
104 inline std::string
105 vstrprintf(const char *format, va_list args)
106 {
107 #ifdef HAVE_VASPRINTF   // This is the preferred method (and safest)
108         char *buffer;
109         std::string ret;
110         int i=vasprintf(&buffer,format,args);
111         if (i>-1)
112         {
113                 ret=buffer;
114                 free(buffer);
115         }
116         return ret;
117 #else
118 #ifdef HAVE_VSNPRINTF   // This is the secondary method (Safe, but bulky)
119 #warning etl::vstrprintf() has a maximum size of ETL_STRPRINTF_MAX_LENGTH in this configuration.
120 #ifdef ETL_THREAD_SAFE
121         char buffer[ETL_STRPRINTF_MAX_LENGTH];
122 #else
123         static char buffer[ETL_STRPRINTF_MAX_LENGTH];
124 #endif
125         vsnprintf(buffer,sizeof(buffer),format,args);
126         return buffer;
127 #else                                   // This is the worst method (UNSAFE, but "works")
128 #warning Potential for Buffer-overflow bug using vsprintf
129 #define ETL_UNSAFE_STRPRINTF    (true)
130 // Here, we are doubling the size of the buffer to make this case
131 // slightly more safe.
132 #ifdef ETL_THREAD_SAFE
133         char buffer[ETL_STRPRINTF_MAX_LENGTH*2];
134 #else
135         static char buffer[ETL_STRPRINTF_MAX_LENGTH*2];
136 #endif
137         vsprintf(buffer,format,args);
138         return buffer;
139 #endif
140 #endif
141 }
142
143 inline std::string
144 strprintf(const char *format, ...)
145 {
146         va_list args;
147         va_start(args,format);
148         return vstrprintf(format,args);
149 }
150
151 #ifndef ETL_NO_VSTRSCANF
152 inline int
153 vstrscanf(const std::string &data, const char*format, va_list args)
154 {
155     return vsscanf(data.c_str(),format,args);
156 }
157
158 inline int
159 strscanf(const std::string &data, const char*format, ...)
160 {
161         va_list args;
162         va_start(args,format);
163         return vstrscanf(data, format,args);
164 }
165 #else
166
167 #if defined (HAVE_SSCANF) && defined (__GNUC__)
168 #define strscanf(data,format,...) sscanf(data.c_str(),format,__VA_ARGS__)
169 #endif
170 #endif
171
172
173 #define stratof(X) (atof((X).c_str()))
174 #define stratoi(X) (atoi((X).c_str()))
175
176 inline std::string
177 basename(const std::string &str)
178 {
179         std::string::const_iterator iter;
180
181         if(str.size() == 1 && str[0] == ETL_DIRECTORY_SEPARATOR)
182                 return str;
183
184         if(str.end()[-1]==ETL_DIRECTORY_SEPARATOR)
185                 iter=str.end()-2;
186         else
187                 iter=str.end()-1;
188
189         for(;iter!=str.begin();iter--)
190                 if(*iter==ETL_DIRECTORY_SEPARATOR)
191                         break;
192
193         if (*iter==ETL_DIRECTORY_SEPARATOR)
194                 iter++;
195
196         if(str.end()[-1]==ETL_DIRECTORY_SEPARATOR)
197                 return std::string(iter,str.end()-1);
198
199         return std::string(iter,str.end());
200 }
201
202 inline std::string
203 dirname(const std::string &str)
204 {
205         std::string::const_iterator iter;
206
207         if(str.size() == 1 && str[0] == ETL_DIRECTORY_SEPARATOR)
208                 return str;
209
210         if(str.end()[-1]==ETL_DIRECTORY_SEPARATOR)
211                 iter=str.end()-2;
212         else
213                 iter=str.end()-1;
214
215         for(;iter!=str.begin();iter--)
216                 if(*iter==ETL_DIRECTORY_SEPARATOR)
217                         break;
218
219         if(iter==str.begin())
220         {
221            if (*iter==ETL_DIRECTORY_SEPARATOR)
222                    return "/";
223            else
224                    return ".";
225         }
226
227         return std::string(str.begin(),iter);
228 }
229
230 // filename_extension("/f.e/d.c") => ".c"
231 inline std::string
232 filename_extension(const std::string &str)
233 {
234         std::string base = basename(str);
235         std::string::size_type pos = base.find_last_of('.');
236         if (pos == std::string::npos) return std::string();
237         return base.substr(pos);
238 }
239
240 // filename_sans_extension("/f.e/d.c") => "/f.e/d"
241 inline std::string
242 filename_sans_extension(const std::string &str)
243 {
244         std::string base = basename(str);
245         std::string::size_type pos = base.find_last_of('.');
246         if (pos == std::string::npos) return str;
247         std::string dir = dirname(str);
248         if (dir == ".") return base.substr(0,pos);
249         return dir + ETL_DIRECTORY_SEPARATOR + base.substr(0,pos);
250 }
251
252 inline bool
253 is_absolute_path(const std::string &path)
254 {
255 #ifdef WIN32
256         if(path.size()>=3 && path[1]==':' && (path[2]=='\\' || path[2]=='/'))
257                 return true;
258 #endif
259         if(!path.empty() && path[0]==ETL_DIRECTORY_SEPARATOR)
260                 return true;
261         return false;
262 }
263
264 inline std::string
265 unix_to_local_path(const std::string &path)
266 {
267         std::string ret;
268         std::string::const_iterator iter;
269         for(iter=path.begin();iter!=path.end();iter++)
270                 switch(*iter)
271                 {
272                 case '/':
273                         ret+=ETL_DIRECTORY_SEPARATOR;
274                         break;
275                 case '~':
276                         ret+='~';
277                         break;
278                 default:
279                         ret+=*iter;
280                         break;
281                 }
282         return ret;
283 }
284
285 inline std::string
286 current_working_directory()
287 {
288         char dir[256];
289         std::string ret(getcwd(dir,sizeof(dir)));
290         return ret;
291 }
292
293 inline std::string
294 get_root_from_path(std::string path)
295 {
296         std::string ret;
297         std::string::const_iterator iter;
298
299         for(iter=path.begin();iter!=path.end();++iter)
300         {
301                 if(*iter==ETL_DIRECTORY_SEPARATOR)
302                         break;
303                 ret+=*iter;
304         }
305         //if(iter!=path.end())
306                 ret+=ETL_DIRECTORY_SEPARATOR;
307         return ret;
308 }
309
310 inline std::string
311 remove_root_from_path(std::string path)
312 {
313         while(!path.empty())
314         {
315                 if(path[0]==ETL_DIRECTORY_SEPARATOR)
316                 {
317                         path.erase(path.begin());
318                         return path;
319                 }
320                 path.erase(path.begin());
321         }
322         return path;
323 }
324
325 inline std::string
326 cleanup_path(std::string path)
327 {
328         std::string ret;
329
330         while(basename(path)=="."&&path.size()!=1)path=dirname(path);
331
332         while(!path.empty())
333         {
334                 std::string dir(get_root_from_path(path));
335                 if((dir=="../" || dir=="..\\") && ret.size())
336                 {
337                         ret=dirname(ret);
338                         if (*(ret.end()-1)!=ETL_DIRECTORY_SEPARATOR)
339                                 ret+=ETL_DIRECTORY_SEPARATOR;
340                 }
341                 else if((dir!="./" && dir!=".\\") && dir!=".")
342                         ret+=dir;
343                 path=remove_root_from_path(path);
344         }
345         if (ret.size()==0)ret+='.';
346
347         // Remove any trailing directory separators
348         if(ret.size() && ret[ret.size()-1]==ETL_DIRECTORY_SEPARATOR)
349                 ret.erase(ret.begin()+ret.size()-1);
350         return ret;
351 }
352
353 inline std::string
354 absolute_path(std::string path)
355 {
356         std::string ret(current_working_directory());
357
358         if(path.empty())
359                 return cleanup_path(ret);
360         if(is_absolute_path(path))
361                 return cleanup_path(path);
362         return cleanup_path(ret+ETL_DIRECTORY_SEPARATOR+path);
363 }
364
365 inline std::string
366 relative_path(std::string curr_path,std::string dest_path)
367 {
368         // If dest_path is already a relative path,
369         // then there is no need to do anything.
370         if(!is_absolute_path(dest_path))
371                 dest_path=absolute_path(dest_path);
372         else
373                 dest_path=cleanup_path(dest_path);
374
375         if(!is_absolute_path(curr_path))
376                 curr_path=absolute_path(curr_path);
377         else
378                 curr_path=cleanup_path(curr_path);
379
380 #ifdef WIN32
381         // If we are on windows and the dest path is on a different drive,
382         // then there is no way to make a relative path to it.
383         if(dest_path.size()>=3 && dest_path[1]==':' && dest_path[0]!=curr_path[0])
384                 return dest_path;
385 #endif
386
387         if(curr_path==dirname(dest_path))
388                 return basename(dest_path);
389
390         while(!dest_path.empty() && !curr_path.empty() && get_root_from_path(dest_path)==get_root_from_path(curr_path))
391         {
392                 dest_path=remove_root_from_path(dest_path);
393                 curr_path=remove_root_from_path(curr_path);
394         }
395
396         while(!curr_path.empty())
397         {
398                 dest_path=std::string("..")+ETL_DIRECTORY_SEPARATOR+dest_path;
399                 curr_path=remove_root_from_path(curr_path);
400         }
401
402         return dest_path;
403 }
404
405 _ETL_END_NAMESPACE
406
407 /* === E N D =============================================================== */
408
409 #endif