1 
2 //
3 // This source file is part of appleseed.
4 // Visit https://appleseedhq.net/ for additional information and resources.
5 //
6 // This software is released under the MIT license.
7 //
8 // Copyright (c) 2010-2013 Francois Beaune, Jupiter Jazz Limited
9 // Copyright (c) 2014-2018 Francois Beaune, The appleseedhq Organization
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining a copy
12 // of this software and associated documentation files (the "Software"), to deal
13 // in the Software without restriction, including without limitation the rights
14 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 // copies of the Software, and to permit persons to whom the Software is
16 // furnished to do so, subject to the following conditions:
17 //
18 // The above copyright notice and this permission notice shall be included in
19 // all copies or substantial portions of the Software.
20 //
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 // THE SOFTWARE.
28 //
29 
30 #pragma once
31 
32 // appleseed.foundation headers.
33 #ifdef _WIN32
34 #include "foundation/platform/windows.h"
35 #endif
36 #include "foundation/utility/iterators.h"
37 #include "foundation/utility/string.h"
38 
39 // appleseed.main headers.
40 #include "main/dllsymbol.h"
41 
42 // Boost headers.
43 #include "boost/filesystem/exception.hpp"
44 #include "boost/filesystem/operations.hpp"
45 #include "boost/filesystem/path.hpp"
46 
47 // Standard headers.
48 #include <cassert>
49 
50 // Platform headers.
51 #if defined __APPLE__ || defined __FreeBSD__
52 #include <sys/param.h>
53 #elif defined __linux__
54 #include <linux/limits.h>
55 #endif
56 
57 namespace foundation
58 {
59 
60 //
61 // The maximum length of a filesystem path.
62 //
63 
64 // Windows.
65 #if defined _WIN32
66     #define FOUNDATION_MAX_PATH_LENGTH  MAX_PATH
67 
68 // macOS.
69 #elif defined __APPLE__ || defined __FreeBSD__
70     #define FOUNDATION_MAX_PATH_LENGTH  MAXPATHLEN
71 
72 // Linux.
73 #elif defined __linux__
74     #define FOUNDATION_MAX_PATH_LENGTH  PATH_MAX
75 
76 // Unsupported platform.
77 #else
78     #error Unsupported platform.
79 #endif
80 
81 
82 //
83 // Common paths.
84 //
85 
86 // Return the path to the application's executable. NOT thread-safe.
87 APPLESEED_DLLSYMBOL const char* get_executable_path();
88 
89 // Return the path to the directory containing the application's executable. NOT thread-safe.
90 APPLESEED_DLLSYMBOL const char* get_executable_directory();
91 
92 // Return the path to the user's home directory.
93 APPLESEED_DLLSYMBOL const char* get_home_directory();
94 
95 
96 //
97 // Operations on boost::filesystem::path objects.
98 //
99 
100 // Try to call boost::filesystem::canonical() on a given path such that it won't fail
101 // if the path does not exist, then call boost::filesystem::path::make_preferred() on
102 // the result.
103 boost::filesystem::path safe_canonical(const boost::filesystem::path& p);
104 
105 // Return true if a path has a non-empty extension.
106 bool has_extension(const boost::filesystem::path& p);
107 
108 // Split two paths into a common base path and two relative paths.
109 void split_paths(
110     const boost::filesystem::path&  p1,
111     const boost::filesystem::path&  p2,
112     boost::filesystem::path&        common,
113     boost::filesystem::path&        r1,
114     boost::filesystem::path&        r2);
115 
116 // Find the next available file path by searching for the first path that does not
117 // refer to an existing file on disk, and where the path follows the given pattern
118 // where consecutive '#' characters have been replaced by increasing integer values
119 // starting with 1.
120 boost::filesystem::path find_next_available_path(const boost::filesystem::path& p);
121 
122 
123 //
124 // Inline implementation of functions using boost::filesystem to allow using them
125 // outside of appleseed's shared library (for instance in appleseed.studio).
126 //
127 
safe_canonical(const boost::filesystem::path & p)128 inline boost::filesystem::path safe_canonical(const boost::filesystem::path& p)
129 {
130     auto result = p;
131 
132     try
133     {
134         result = boost::filesystem::canonical(result);
135     }
136     catch (const boost::filesystem::filesystem_error&)
137     {
138     }
139 
140     return result.make_preferred();
141 }
142 
has_extension(const boost::filesystem::path & p)143 inline bool has_extension(const boost::filesystem::path& p)
144 {
145     const auto ext = p.extension();
146     return !ext.empty() && ext != ".";
147 }
148 
split_paths(const boost::filesystem::path & p1,const boost::filesystem::path & p2,boost::filesystem::path & common,boost::filesystem::path & r1,boost::filesystem::path & r2)149 inline void split_paths(
150     const boost::filesystem::path&  p1,
151     const boost::filesystem::path&  p2,
152     boost::filesystem::path&        common,
153     boost::filesystem::path&        r1,
154     boost::filesystem::path&        r2)
155 {
156     assert(common.empty());
157     assert(r1.empty());
158     assert(r2.empty());
159 
160     auto i1 = p1.begin();
161     auto i2 = p2.begin();
162 
163     while (i1 != p1.end() && i2 != p2.end())
164     {
165         if (*i1 != *i2)
166             break;
167 
168         if ((p1.has_filename() && succ(i1) == p1.end()) ||
169             (p2.has_filename() && succ(i2) == p2.end()))
170             break;
171 
172         common /= *i1;
173 
174         ++i1, ++i2;
175     }
176 
177     while (i1 != p1.end())
178         r1 /= *i1++;
179 
180     while (i2 != p2.end())
181         r2 /= *i2++;
182 }
183 
find_next_available_path(const boost::filesystem::path & p)184 inline boost::filesystem::path find_next_available_path(const boost::filesystem::path& p)
185 {
186     const auto pattern = p.string();
187     const auto max_value = get_numbered_string_max_value(pattern);
188 
189     for (size_t value = 1; value <= max_value; ++value)
190     {
191         const boost::filesystem::path candidate(get_numbered_string(pattern, value));
192 
193         if (!boost::filesystem::exists(candidate))
194             return candidate;
195     }
196 
197     return boost::filesystem::path(get_numbered_string(pattern, 1));
198 }
199 
200 }   // namespace foundation
201