2 * Copyright 2003 Maurizio Umberto Puxeddu
3 * Copyright 2011 Jakob Leben
5 * This file is part of SuperCollider.
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.
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.
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
24 #include "SC_LibraryConfig.h"
26 #include "SC_StringBuffer.h"
27 #include "SC_DirUtils.h"
36 # include "SC_Win32Utils.h"
38 # include <sys/param.h>
44 #include "yaml-cpp/yaml.h"
48 SC_LanguageConfig
*gLibraryConfig
= 0;
50 void SC_LanguageConfig::postExcludedDirectories(void)
52 DirVector
&vec
= mExcludedDirectories
;
53 DirVector::iterator it
;
54 for (it
=vec
.begin(); it
!=vec
.end(); ++it
) {
55 post("\texcluding dir: '%s'\n", it
->c_str());
59 bool SC_LanguageConfig::forEachIncludedDirectory(bool (*func
)(const char *, int))
61 DirVector
&vec
= mIncludedDirectories
;
62 DirVector::iterator it
;
63 for (it
=vec
.begin(); it
!=vec
.end(); ++it
) {
64 if (!func(it
->c_str(), 0)) return false;
69 static bool findPath( SC_LanguageConfig::DirVector
& vec
, const char * path
, bool addIfMissing
)
71 char standardPath
[PATH_MAX
];
72 sc_StandardizePath(path
, standardPath
);
74 for ( SC_LanguageConfig::DirVector::iterator it
= vec
.begin(); it
!= vec
.end(); ++it
)
75 if (!strcmp(standardPath
, it
->c_str()))
79 vec
.push_back(string(standardPath
));
84 bool SC_LanguageConfig::pathIsExcluded(const char *path
)
86 return findPath(mExcludedDirectories
, path
, false);
89 void SC_LanguageConfig::addIncludedDirectory(const char *path
)
91 if (path
== 0) return;
92 findPath(mIncludedDirectories
, path
, true);
95 void SC_LanguageConfig::addExcludedDirectory(const char *path
)
97 if (path
== 0) return;
98 findPath(mExcludedDirectories
, path
, true);
101 void SC_LanguageConfig::removeIncludedDirectory(const char *path
)
103 char standardPath
[PATH_MAX
];
104 sc_StandardizePath(path
, standardPath
);
105 string
str(standardPath
);
106 DirVector::iterator end
= std::remove(mIncludedDirectories
.begin(), mIncludedDirectories
.end(), str
);
107 mIncludedDirectories
.erase(end
, mIncludedDirectories
.end());
110 void SC_LanguageConfig::removeExcludedDirectory(const char *path
)
113 DirVector::iterator end
= std::remove(mExcludedDirectories
.begin(), mExcludedDirectories
.end(), str
);
114 mExcludedDirectories
.erase(end
, mExcludedDirectories
.end());
117 bool SC_LanguageConfig::readLibraryConfig(const char* fileName
)
120 gLibraryConfig
= new SC_LanguageConfig();
122 SC_LibraryConfigFile
file(::post
);
123 bool success
= file
.open(fileName
);
127 bool error
= file
.read(fileName
, gLibraryConfig
);
137 extern bool gPostInlineWarnings
;
138 bool SC_LanguageConfig::readLibraryConfigYAML(const char* fileName
)
141 gLibraryConfig
= new SC_LanguageConfig();
143 using namespace YAML
;
145 std::ifstream
fin(fileName
);
149 while(parser
.GetNextDocument(doc
)) {
150 const Node
* includePaths
= doc
.FindValue("includePaths");
151 if (includePaths
&& includePaths
->Type() == NodeType::Sequence
) {
152 for (Iterator it
= includePaths
->begin(); it
!= includePaths
->end(); ++it
) {
153 Node
const & pathNode
= *it
;
154 if (pathNode
.Type() != NodeType::Scalar
)
157 pathNode
.GetScalar(path
);
158 gLibraryConfig
->addIncludedDirectory(path
.c_str());
162 const Node
* excludePaths
= doc
.FindValue("excludePaths");
163 if (excludePaths
&& excludePaths
->Type() == NodeType::Sequence
) {
164 for (Iterator it
= excludePaths
->begin(); it
!= excludePaths
->end(); ++it
) {
165 Node
const & pathNode
= *it
;
166 if (pathNode
.Type() != NodeType::Scalar
)
169 pathNode
.GetScalar(path
);
170 gLibraryConfig
->addExcludedDirectory(path
.c_str());
174 const Node
* inlineWarnings
= doc
.FindValue("postInlineWarnings");
175 if (inlineWarnings
) {
177 gPostInlineWarnings
= inlineWarnings
->to
<bool>();
179 postfl("Warning: Cannot parse config file entry \"postInlineWarnings\"\n");
184 } catch (std::exception
& e
)
186 postfl("Exception when parsing YAML config file: %s\n", e
.what());
192 bool SC_LanguageConfig::writeLibraryConfigYAML(const char* fileName
)
194 using namespace YAML
;
197 out
.SetMapFormat(Block
);
198 out
.SetSeqFormat(Block
);
199 out
.SetBoolFormat(TrueFalseBool
);
203 out
<< Key
<< "includePaths";
204 out
<< Value
<< BeginSeq
;
205 for (DirVector::iterator it
= gLibraryConfig
->mIncludedDirectories
.begin();
206 it
!= gLibraryConfig
->mIncludedDirectories
.end(); ++it
)
210 out
<< Key
<< "excludePaths";
211 out
<< Value
<< BeginSeq
;
212 for (DirVector::iterator it
= gLibraryConfig
->mExcludedDirectories
.begin();
213 it
!= gLibraryConfig
->mExcludedDirectories
.end(); ++it
)
217 out
<< Key
<< "postInlineWarnings";
218 out
<< Value
<< gPostInlineWarnings
;
221 ofstream
fout(fileName
);
226 bool SC_LanguageConfig::defaultLibraryConfig(void)
229 gLibraryConfig
= new SC_LanguageConfig();
231 char compileDir
[MAXPATHLEN
];
232 char systemExtensionDir
[MAXPATHLEN
];
233 char userExtensionDir
[MAXPATHLEN
];
235 sc_GetResourceDirectory(compileDir
, MAXPATHLEN
-32);
236 sc_AppendToPath(compileDir
, MAXPATHLEN
, "SCClassLibrary");
237 gLibraryConfig
->addIncludedDirectory(compileDir
);
239 if (!sc_IsStandAlone()) {
240 sc_GetSystemExtensionDirectory(systemExtensionDir
, MAXPATHLEN
);
241 gLibraryConfig
->addIncludedDirectory(systemExtensionDir
);
243 sc_GetUserExtensionDirectory(userExtensionDir
, MAXPATHLEN
);
244 gLibraryConfig
->addIncludedDirectory(userExtensionDir
);
249 static bool file_exists(const char * fileName
)
251 FILE * fp
= fopen(fileName
, "r");
257 static bool file_exists(std::string
const & fileName
)
259 return file_exists(fileName
.c_str());
263 bool SC_LanguageConfig::readDefaultLibraryConfig()
265 char config_dir
[PATH_MAX
];
266 bool configured
= false;
267 sc_GetUserConfigDirectory(config_dir
, PATH_MAX
);
269 std::string user_yaml_config_file
= std::string(config_dir
) + SC_PATH_DELIMITER
+ "sclang_conf.yaml";
270 if (file_exists(user_yaml_config_file
))
271 configured
= readLibraryConfigYAML(user_yaml_config_file
.c_str());
274 char global_yaml_config_file
[] = "/etc/sclang_conf.yaml";
275 if (file_exists(global_yaml_config_file
))
276 configured
= readLibraryConfigYAML(global_yaml_config_file
);
279 std::string config_file
= std::string(config_dir
) + SC_PATH_DELIMITER
+ "sclang.cfg";
281 // deprecated config files
282 const char* paths
[4] = { config_file
.c_str(), ".sclang.cfg", "~/.sclang.cfg", "/etc/sclang.cfg"};
284 bool deprecatedConfigFileDetected
= false;
285 for (int i
=0; i
< 4; i
++) {
286 const char * ipath
= paths
[i
];
287 char opath
[PATH_MAX
];
288 if (sc_StandardizePath(ipath
, opath
)) {
290 if (file_exists(opath
)) {
291 deprecatedConfigFileDetected
= true;
292 postfl("reading deprecated config file: %s\n", opath
);
293 configured
= readLibraryConfig(opath
);
299 if (deprecatedConfigFileDetected
)
300 postfl("Please migrate your sclang config file to %s.\n", user_yaml_config_file
.c_str());
305 SC_LanguageConfig::defaultLibraryConfig();
310 void SC_LanguageConfig::freeLibraryConfig()
312 if (gLibraryConfig
) {
313 delete gLibraryConfig
;
318 // =====================================================================
319 // SC_LibraryConfigFile
320 // =====================================================================
322 SC_LibraryConfigFile::SC_LibraryConfigFile(ErrorFunc errorFunc
)
323 : mErrorFunc(errorFunc
? errorFunc
: &defaultErrorFunc
),
327 bool SC_LibraryConfigFile::open(const char* filePath
)
331 mFile
= fopen(filePath
, "rb");
333 mFile
= fopen(filePath
, "r");
338 void SC_LibraryConfigFile::close()
346 bool SC_LibraryConfigFile::read(const char* fileName
, SC_LanguageConfig
* libConf
)
348 return read(0, fileName
, libConf
);
351 bool SC_LibraryConfigFile::read(int depth
, const char* fileName
, SC_LanguageConfig
* libConf
)
353 if (!mFile
) return false;
356 size_t lineNumber
= 1;
357 SC_StringBuffer line
;
360 int c
= fgetc(mFile
);
363 if (eof
|| (c
== '\n')) {
365 // go on if line parse failed
366 error
|= parseLine(depth
, fileName
, lineNumber
, line
.getData(), libConf
);
378 bool SC_LibraryConfigFile::parseLine(int depth
, const char* fileName
, int lineNumber
, const char* line
, SC_LanguageConfig
* libConf
)
381 SC_StringBuffer path
;
382 SC_StringBuffer envVarName
;
383 State state
= kBegin
;
386 // NOTE: in some parser states the character just read is
387 // written back to be consumed by the following state in the
388 // next iteration; this may be slightly inefficient, but makes
389 // control flow more obvious.
393 if ((c
== '\0') || ((c
== '#') && (state
!= kEscape
))) {
405 if ((c
== '+') || (c
== '-') || (c
== ':')) {
409 (*mErrorFunc
)("%s,%d: invalid action '%c'\n", fileName
, lineNumber
, c
);
416 } else if (c
== '$') {
418 } else if (isspace(c
)) {
434 (*mErrorFunc
)("%s,%d: empty variable reference\n", fileName
, lineNumber
);
439 if (isalpha(c
) || (c
== '_')) {
440 envVarName
.append(c
);
443 char* envVarValue
= getenv(envVarName
.getData());
447 path
.append(envVarValue
);
449 (*mErrorFunc
)("%s,%d: undefined variable '%s'\n", fileName
, lineNumber
, envVarName
.getData());
456 (*mErrorFunc
)("%s,%d: trailing garbage\n", fileName
, lineNumber
);
461 (*mErrorFunc
)("%s,%d: [internal error] invalid parser state %d\n", fileName
, lineNumber
, state
);
466 if (!action
) return true;
468 if (path
.getSize() == 0) {
469 (*mErrorFunc
)("%s,%d: empty path\n", fileName
, lineNumber
);
474 char realPath
[MAXPATHLEN
];
476 if (sc_StandardizePath(path
.getData(), realPath
) == 0) {
477 (*mErrorFunc
)("%s,%d: couldn't resolve path %s\n", fileName
, lineNumber
, path
.getData());
482 if (++depth
> kMaxIncludeDepth
) {
483 (*mErrorFunc
)("%s,%d: maximum include depth of %d exceeded\n", fileName
, lineNumber
, kMaxIncludeDepth
);
486 SC_LibraryConfigFile
file(mErrorFunc
);
487 if (!file
.open(realPath
)) return true;
488 const char* fileName
= basename(realPath
);
489 bool success
= file
.read(depth
, fileName
, libConf
);
495 libConf
->addIncludedDirectory(realPath
);
496 } else if (action
== '-') {
497 libConf
->addExcludedDirectory(realPath
);
503 void SC_LibraryConfigFile::defaultErrorFunc(const char* fmt
, ...)