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