1 /*
2 * Copyright 2003 Maurizio Umberto Puxeddu
3 * Copyright 2011 Jakob Leben
4 *
5 * This file is part of SuperCollider.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
20 * USA
21 *
22 */
23
24 #include "SC_LanguageConfig.hpp"
25 #include "SC_Filesystem.hpp" // getDirectory
26
27 #include "SCBase.h" // postfl
28
29 #include <algorithm> // std::find
30
31 #include <boost/filesystem/operations.hpp> // exists (, canonical?)
32 #include <boost/filesystem/fstream.hpp> // ofstream
33 #include <yaml-cpp/yaml.h> // YAML
34
35 SC_LanguageConfig::Path SC_LanguageConfig::gConfigFile;
36 bool SC_LanguageConfig::gPostInlineWarnings = false;
37
38 SC_LanguageConfig* gLanguageConfig;
39
40 static const char* INCLUDE_PATHS = "includePaths";
41 static const char* EXCLUDE_PATHS = "excludePaths";
42 static const char* POST_INLINE_WARNINGS = "postInlineWarnings";
43 static const char* CLASS_LIB_DIR_NAME = "SCClassLibrary";
44 const char* SCLANG_YAML_CONFIG_FILENAME = "sclang_conf.yaml";
45
46 using DirName = SC_Filesystem::DirName;
47 namespace bfs = boost::filesystem;
48
SC_LanguageConfig(bool optStandalone)49 SC_LanguageConfig::SC_LanguageConfig(bool optStandalone) {
50 if (!optStandalone) {
51 const Path& classLibraryDir = SC_Filesystem::instance().getDirectory(DirName::Resource) / CLASS_LIB_DIR_NAME;
52 addPath(mDefaultClassLibraryDirectories, classLibraryDir);
53
54 const Path& systemExtensionDir = SC_Filesystem::instance().getDirectory(DirName::SystemExtension);
55 addPath(mDefaultClassLibraryDirectories, systemExtensionDir);
56
57 const Path& userExtensionDir = SC_Filesystem::instance().getDirectory(DirName::UserExtension);
58 addPath(mDefaultClassLibraryDirectories, userExtensionDir);
59 }
60 }
61
postExcludedDirectories(void) const62 void SC_LanguageConfig::postExcludedDirectories(void) const {
63 for (auto it : mExcludedDirectories) {
64 post("\texcluding dir: '%s'\n", it.c_str());
65 }
66 }
67
forEachIncludedDirectory(bool (* func)(const Path &)) const68 bool SC_LanguageConfig::forEachIncludedDirectory(bool (*func)(const Path&)) const {
69 for (const auto& it : mDefaultClassLibraryDirectories) {
70 if (!pathIsExcluded(it)) {
71 if (!func(it))
72 return false;
73 }
74 }
75
76 for (const auto& it : mIncludedDirectories) {
77 if (!pathIsExcluded(it)) {
78 if (!func(it))
79 return false;
80 }
81 }
82
83 return true;
84 }
85
pathIsExcluded(const Path & path) const86 bool SC_LanguageConfig::pathIsExcluded(const Path& path) const { return findPath(mExcludedDirectories, path); }
87
addIncludedDirectory(const Path & path)88 bool SC_LanguageConfig::addIncludedDirectory(const Path& path) { return addPath(mIncludedDirectories, path); }
89
addExcludedDirectory(const Path & path)90 bool SC_LanguageConfig::addExcludedDirectory(const Path& path) { return addPath(mExcludedDirectories, path); }
91
removeIncludedDirectory(const Path & path)92 bool SC_LanguageConfig::removeIncludedDirectory(const Path& path) { return removePath(mIncludedDirectories, path); }
93
removeExcludedDirectory(const Path & path)94 bool SC_LanguageConfig::removeExcludedDirectory(const Path& path) { return removePath(mExcludedDirectories, path); }
95
readLibraryConfigYAML(const Path & fileName,bool standalone)96 bool SC_LanguageConfig::readLibraryConfigYAML(const Path& fileName, bool standalone) {
97 freeLibraryConfig();
98 gLanguageConfig = new SC_LanguageConfig(standalone);
99
100 std::string emptyString;
101
102 using namespace YAML;
103 try {
104 bfs::ifstream fin(fileName);
105 Node doc = Load(fin);
106 if (doc) {
107 const Node& includePaths = doc[INCLUDE_PATHS];
108 if (includePaths && includePaths.IsSequence()) {
109 for (auto const& pathNode : includePaths) {
110 const std::string& path = pathNode.as<std::string>(emptyString);
111 if (!path.empty()) {
112 const Path& native_path = SC_Codecvt::utf8_str_to_path(path);
113 gLanguageConfig->addIncludedDirectory(native_path);
114 }
115 }
116 }
117
118 const Node& excludePaths = doc[EXCLUDE_PATHS];
119 if (excludePaths && excludePaths.IsSequence()) {
120 for (auto const& pathNode : excludePaths) {
121 const std::string& path = pathNode.as<std::string>(emptyString);
122 if (!path.empty()) {
123 const Path& native_path = SC_Codecvt::utf8_str_to_path(path);
124 gLanguageConfig->addExcludedDirectory(native_path);
125 }
126 }
127 }
128
129 const Node& inlineWarnings = doc[POST_INLINE_WARNINGS];
130 if (inlineWarnings) {
131 try {
132 gPostInlineWarnings = inlineWarnings.as<bool>();
133 } catch (...) {
134 postfl("WARNING: Cannot parse config file entry \"%s\"\n", POST_INLINE_WARNINGS);
135 }
136 }
137 }
138 return true;
139 } catch (std::exception& e) {
140 postfl("Exception while parsing YAML config file: %s\n", e.what());
141 freeLibraryConfig();
142 return false;
143 }
144 }
145
writeLibraryConfigYAML(const Path & fileName)146 bool SC_LanguageConfig::writeLibraryConfigYAML(const Path& fileName) {
147 if (!bfs::exists(fileName.parent_path()))
148 return false;
149
150 using namespace YAML;
151 Emitter out;
152 out.SetIndent(4);
153 out.SetMapFormat(Block);
154 out.SetSeqFormat(Block);
155 out.SetBoolFormat(TrueFalseBool);
156
157 out << BeginMap;
158
159 out << Key << INCLUDE_PATHS;
160 out << Value << BeginSeq;
161 for (const bfs::path& it : gLanguageConfig->mIncludedDirectories)
162 out << SC_Codecvt::path_to_utf8_str(it);
163 out << EndSeq;
164
165 out << Key << EXCLUDE_PATHS;
166 out << Value << BeginSeq;
167 for (const bfs::path& it : gLanguageConfig->mExcludedDirectories)
168 out << SC_Codecvt::path_to_utf8_str(it);
169 out << EndSeq;
170
171 out << Key << POST_INLINE_WARNINGS;
172 out << Value << gPostInlineWarnings;
173
174 out << EndMap;
175
176 bfs::ofstream fout(fileName);
177 fout << out.c_str();
178 return fout.good();
179 }
180
defaultLibraryConfig(bool standalone)181 bool SC_LanguageConfig::defaultLibraryConfig(bool standalone) {
182 freeLibraryConfig();
183 gLanguageConfig = new SC_LanguageConfig(standalone);
184
185 return true;
186 }
187
readLibraryConfig(bool standalone)188 bool SC_LanguageConfig::readLibraryConfig(bool standalone) {
189 bool configured = false;
190
191 if (bfs::exists(gConfigFile))
192 configured = readLibraryConfigYAML(gConfigFile, standalone);
193
194 if (!configured && !standalone) {
195 const Path userYamlConfigFile =
196 SC_Filesystem::instance().getDirectory(DirName::UserConfig) / SCLANG_YAML_CONFIG_FILENAME;
197
198 if (bfs::exists(userYamlConfigFile))
199 configured = readLibraryConfigYAML(userYamlConfigFile, standalone);
200
201 if (!configured) {
202 const Path globalYamlConfigFile = Path("/etc") / SCLANG_YAML_CONFIG_FILENAME;
203
204 if (bfs::exists(globalYamlConfigFile))
205 configured = readLibraryConfigYAML(globalYamlConfigFile, standalone);
206 }
207 }
208
209 if (!configured)
210 configured = SC_LanguageConfig::defaultLibraryConfig(standalone);
211
212 return configured;
213 }
214
freeLibraryConfig()215 void SC_LanguageConfig::freeLibraryConfig() {
216 if (gLanguageConfig) {
217 delete gLanguageConfig;
218 gLanguageConfig = nullptr;
219 }
220 }
221
findPath(const DirVector & vec,const Path & path)222 bool SC_LanguageConfig::findPath(const DirVector& vec, const Path& path) {
223 return std::find(vec.begin(), vec.end(), path) != vec.end();
224 }
225
addPath(DirVector & vec,const Path & path)226 bool SC_LanguageConfig::addPath(DirVector& vec, const Path& path) {
227 if (!findPath(vec, path)) {
228 vec.push_back(path);
229 return true;
230 } else {
231 return false;
232 }
233 }
234
removePath(DirVector & vec,const Path & path)235 bool SC_LanguageConfig::removePath(DirVector& vec, const Path& path) {
236 const DirVector::iterator& end = std::remove(vec.begin(), vec.end(), path);
237 const bool removed = end != vec.end();
238 vec.erase(end, vec.end());
239 return removed;
240 }
241