1 /*
2  * ====================================================================
3  * Copyright (c) 2002-2009 The RapidSvn Group.  All rights reserved.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program (in the file GPL.txt.
17  * If not, see <http://www.gnu.org/licenses/>.
18  *
19  * This software consists of voluntary contributions made by many
20  * individuals.  For exact contribution history, see the revision
21  * history and logs, available at http://rapidsvn.tigris.org/.
22  * ====================================================================
23  */
24 
25 // subversion api
26 #include "svn_path.h"
27 #include "svn_dirent_uri.h"
28 
29 // apr api
30 #include "apr_file_io.h"
31 
32 // svncpp
33 #include "kdevsvncpp/path.hpp"
34 #include "kdevsvncpp/pool.hpp"
35 #include "kdevsvncpp/url.hpp"
36 
37 namespace svn
38 {
39   const PathVector EmptyPathVector;
40 
Path(const char * path)41   Path::Path(const char * path)
42   {
43     init(path);
44   }
45 
Path(const std::string & path)46   Path::Path(const std::string & path)
47   {
48     init(path.c_str());
49   }
50 
Path(const Path & path)51   Path::Path(const Path & path)
52   {
53     init(path.c_str());
54   }
55 
56   void
init(const char * path)57   Path::init(const char * path)
58   {
59     Pool pool;
60 
61     m_pathIsUrl = false;
62 
63     if (path == nullptr)
64       m_path = "";
65     else
66     {
67       const char * int_path = svn_dirent_canonicalize(path, pool);
68 
69       m_path = int_path;
70 
71       if (svn::Url::isValid(int_path))
72         m_pathIsUrl = true;
73     }
74   }
75 
76   const std::string &
path() const77   Path::path() const
78   {
79     return m_path;
80   }
81 
82   const char *
c_str() const83   Path::c_str() const
84   {
85     return m_path.c_str();
86   }
87 
88   Path&
operator =(const Path & path)89   Path::operator= (const Path & path)
90   {
91     if (this == &path)
92       return *this;
93 
94     init(path.c_str());
95 
96     return *this;
97   }
98 
99   bool
operator ==(const Path & path) const100   Path::operator== (const Path& path) const
101   {
102     if (path.path() == this->path())
103       return true;
104 
105     return false;
106   }
107 
108   bool
isSet() const109   Path::isSet() const
110   {
111     return m_path.length() > 0;
112   }
113 
114   bool
isUrl() const115   Path::isUrl() const
116   {
117     return m_pathIsUrl;
118   }
119 
120   static bool
isAbsolute(const char * path)121   isAbsolute(const char * path)
122   {
123     if (nullptr == path)
124       return false;
125 
126     std::string p(path);
127 
128     if (0 == p.length())
129       return false;
130 
131     // a path that begins with "/" is absolute
132     if ('/' == p [0])
133       return true;
134 
135     // a path with a ":" like "http://xxx" or
136     // "c:/foo" is absolute too
137     if (p.find(":", 0) != std::string::npos)
138       return true;
139 
140     // Well it's relative
141     return false;
142   }
143 
144   void
addComponent(const char * component)145   Path::addComponent(const char * component)
146   {
147     Pool pool;
148 
149     if (nullptr == component)
150       return;
151 
152     // in case of an empty string, return
153     if (*component == 0)
154       return;
155 
156     // if the @a component is absolute, simply
157     // use it
158     if (isAbsolute(component))
159     {
160       m_path = component;
161       return;
162     }
163 
164     if (Url::isValid(m_path.c_str()))
165     {
166       const char * newPath =
167         svn_path_url_add_component(m_path.c_str(),
168                                    component,
169                                    pool);
170       m_path = newPath;
171     }
172     else
173     {
174       svn_stringbuf_t * pathStringbuf =
175         svn_stringbuf_create(m_path.c_str(), pool);
176 
177       svn_path_add_component(pathStringbuf,
178                              component);
179 
180       m_path = pathStringbuf->data;
181     }
182   }
183 
184   void
addComponent(const std::string & component)185   Path::addComponent(const std::string & component)
186   {
187     addComponent(component.c_str());
188   }
189 
190   void
split(std::string & dirpath,std::string & basename) const191   Path::split(std::string & dirpath, std::string & basename) const
192   {
193     Pool pool;
194 
195     const char * cdirpath;
196     const char * cbasename;
197 
198     svn_path_split(m_path.c_str(), &cdirpath, &cbasename, pool);
199 
200     dirpath = cdirpath;
201     basename = cbasename;
202   }
203 
204   void
split(std::string & dir,std::string & filename,std::string & ext) const205   Path::split(std::string & dir, std::string & filename, std::string & ext) const
206   {
207     std::string basename;
208 
209     // first split path into dir and filename+ext
210     split(dir, basename);
211 
212     // next search for last .
213     size_t pos = basename.find_last_of(".");
214     if (pos == std::string::npos)
215     {
216       filename = basename;
217       ext = "";
218     }
219     else
220     {
221       filename = basename.substr(0, pos);
222       ext = basename.substr(pos);
223     }
224   }
225 
226   std::string
basename() const227   Path::basename() const
228   {
229     std::string dir;
230     std::string filename;
231 
232     split(dir, filename);
233 
234     return filename;
235   }
236 
237   std::string
dirpath() const238   Path::dirpath() const
239   {
240     std::string dir;
241     std::string filename;
242 
243     split(dir, filename);
244 
245     return dir;
246   }
247 
248   std::string
substr(const size_t count) const249   Path::substr(const size_t count) const
250   {
251     if (m_path.length() > count)
252       return m_path.substr(count);
253     else
254       return "";
255   }
256 
257   std::string
unescape() const258   Path::unescape() const
259   {
260     return svn::Url::unescape(m_path.c_str());
261   }
262 
263 
264   /* ===================================================================
265    * The next two Fixed_* functions are copies of the APR
266    * apr_temp_dir_get functionality with a fix applied.
267    * This should turn up in APR release 0.9.5 or 1.0, but
268    * for now is reproduced here.
269    *
270    * TODO: Remove this section!
271    */
272 #include "apr_env.h"
273 
274 #define test_tempdir    Fixed_test_tempdir
275 #define apr_temp_dir_get    Fixed_apr_temp_dir_get
276 
277   static char global_temp_dir[APR_PATH_MAX+1] = { 0 };
278 
279   /* Try to open a temporary file in the temporary dir, write to it,
280     and then close it. */
Fixed_test_tempdir(const char * temp_dir,apr_pool_t * p)281   static int Fixed_test_tempdir(const char *temp_dir, apr_pool_t *p)
282   {
283     apr_file_t *dummy_file;
284     // This is the only actual fix - adding the ".XXXXXX"!
285     const char *path = apr_pstrcat(p, temp_dir, "/apr-tmp.XXXXXX", NULL);
286 
287     if (apr_file_mktemp(&dummy_file, (char *)path, 0, p) == APR_SUCCESS) {
288       if (apr_file_putc('!', dummy_file) == APR_SUCCESS) {
289         if (apr_file_close(dummy_file) == APR_SUCCESS) {
290           apr_file_remove(path, p);
291           return 1;
292         }
293       }
294     }
295     return 0;
296   }
297 
Fixed_apr_temp_dir_get(const char ** temp_dir,apr_pool_t * p)298   static apr_status_t Fixed_apr_temp_dir_get(const char **temp_dir, apr_pool_t *p)
299   {
300     apr_status_t apr_err;
301     const char *try_dirs[] = { "/tmp", "/usr/tmp", "/var/tmp" };
302     const char *try_envs[] = { "TMP", "TEMP", "TMPDIR" };
303     char *cwd;
304     size_t i;
305 
306     /* Our goal is to find a temporary directory suitable for writing
307        into.  We'll only pay the price once if we're successful -- we
308        cache our successful find.  Here's the order in which we'll try
309        various paths:
310 
311           $TMP
312           $TEMP
313           $TMPDIR
314           "/tmp"
315           "/var/tmp"
316           "/usr/tmp"
317           `pwd`
318 
319        NOTE: This algorithm is basically the same one used by Python
320        2.2's tempfile.py module.  */
321 
322     /* Try the environment first. */
323     for (i = 0; i < (sizeof(try_envs) / sizeof(const char *)); i++) {
324       char *value;
325       apr_err = apr_env_get(&value, try_envs[i], p);
326       if ((apr_err == APR_SUCCESS) && value) {
327         apr_size_t len = strlen(value);
328         if (len && (len < APR_PATH_MAX) && test_tempdir(value, p)) {
329           memcpy(global_temp_dir, value, len + 1);
330           goto end;
331         }
332       }
333     }
334 
335     /* Next, try a set of hard-coded paths. */
336     for (i = 0; i < (sizeof(try_dirs) / sizeof(const char *)); i++) {
337       if (test_tempdir(try_dirs[i], p)) {
338         memcpy(global_temp_dir, try_dirs[i], strlen(try_dirs[i]) + 1);
339         goto end;
340       }
341     }
342 
343     /* Finally, try the current working directory. */
344     if (APR_SUCCESS == apr_filepath_get(&cwd, APR_FILEPATH_NATIVE, p)) {
345       if (test_tempdir(cwd, p)) {
346         memcpy(global_temp_dir, cwd, strlen(cwd) + 1);
347         goto end;
348       }
349     }
350 
351 end:
352     if (global_temp_dir[0]) {
353       *temp_dir = apr_pstrdup(p, global_temp_dir);
354       return APR_SUCCESS;
355     }
356     return APR_EGENERAL;
357   }
358   /* ===================================================================
359    * End of inserted fixed APR code
360    */
361 
362   Path
getTempDir()363   Path::getTempDir()
364   {
365     const char * tempdir = nullptr;
366     Pool pool;
367 
368     if (apr_temp_dir_get(&tempdir, pool) != APR_SUCCESS)
369     {
370       tempdir = nullptr;
371     }
372 
373     return tempdir;
374   }
375 
376   size_t
length() const377   Path::length() const
378   {
379     return m_path.length();
380   }
381 
382   std::string
native() const383   Path::native() const
384   {
385     if (m_pathIsUrl)
386     {
387       // this converts something like
388       // http://foo/my%20location
389       // to
390       // http://foo/my location
391 	    return Url::unescape(m_path.c_str());
392     }
393     else
394     {
395       // On Windows, p://foo/bar will be converted to p:\foo\bar
396 	    Pool pool;
397       return svn_path_local_style(m_path.c_str(), pool);
398     }
399   }
400 }
401 
402 /* -----------------------------------------------------------------
403  * local variables:
404  * eval: (load-file "../../rapidsvn-dev.el")
405  * end:
406  */
407