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