1 /************************************************************************
2 ************************************************************************
3 FAUST compiler
4 Copyright (C) 2003-2018 GRAME, Centre National de Creation Musicale
5 ---------------------------------------------------------------------
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 ************************************************************************
20 ************************************************************************/
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <fstream>
26 #include <iostream>
27 #include <sstream>
28
29 #include "Text.hh"
30 #include "sha_key.hh"
31 #include "compatibility.hh"
32 #include "dsp_aux.hh"
33 #include "dsp_factory.hh"
34 #include "lock_api.hh"
35 #include "libfaust.h"
36
37 #ifdef WIN32
38 #pragma warning(disable : 4996)
39 #endif
40
41 using namespace std;
42
43 // Look for 'key' in 'options' and modify the parameter 'position' if found
parseKey(vector<string> & options,const string & key,int & position)44 static bool parseKey(vector<string>& options, const string& key, int& position)
45 {
46 for (int i = 0; i < int(options.size()); i++) {
47 if (key == options[i]) {
48 position = i;
49 return true;
50 }
51 }
52
53 return false;
54 }
55
56 /*
57 * Add 'key' if existing in 'options', otherwise add 'defaultKey' (if different from "")
58 * return true if 'key' was added
59 */
addKeyIfExisting(vector<string> & options,vector<string> & newoptions,const string & key,const string & defaultKey,int & position)60 static bool addKeyIfExisting(vector<string>& options, vector<string>& newoptions, const string& key,
61 const string& defaultKey, int& position)
62 {
63 if (parseKey(options, key, position)) {
64 newoptions.push_back(options[position]);
65 options.erase(options.begin() + position);
66 position--;
67 return true;
68 } else if (defaultKey != "") {
69 newoptions.push_back(defaultKey);
70 }
71
72 return false;
73 }
74
75 // Add 'key' & it's associated value if existing in 'options', otherwise add 'defaultValue' (if different from "")
addKeyValueIfExisting(vector<string> & options,vector<string> & newoptions,const string & key,const string & defaultValue)76 static void addKeyValueIfExisting(vector<string>& options, vector<string>& newoptions, const string& key,
77 const string& defaultValue)
78 {
79 int position = 0;
80
81 if (addKeyIfExisting(options, newoptions, key, "", position)) {
82 if (position + 1 < int(options.size()) && options[position + 1][0] != '-') {
83 newoptions.push_back(options[position + 1]);
84 options.erase(options.begin() + position + 1);
85 position--;
86 } else {
87 newoptions.push_back(defaultValue);
88 }
89 }
90 }
91
92 /*
93 * Reorganizes the compilation options
94 * Following the tree of compilation (Faust_Compilation_Options.pdf in distribution)
95 */
reorganizeCompilationOptionsAux(vector<string> & options)96 static vector<string> reorganizeCompilationOptionsAux(vector<string>& options)
97 {
98 bool vectorize = false;
99 int position = 0;
100
101 vector<string> newoptions;
102
103 //------STEP 1 - Single or Double ?
104 addKeyIfExisting(options, newoptions, "-double", "-single", position);
105
106 //------STEP 2 - Options Leading to -vec inclusion
107 if (addKeyIfExisting(options, newoptions, "-sch", "", position)) {
108 vectorize = true;
109 }
110
111 if (addKeyIfExisting(options, newoptions, "-omp", "", position)) {
112 vectorize = true;
113 addKeyIfExisting(options, newoptions, "-pl", "", position);
114 }
115
116 if (vectorize) {
117 newoptions.push_back("-vec");
118 }
119
120 //------STEP3 - Add options depending on -vec/-scal option
121 if (vectorize || addKeyIfExisting(options, newoptions, "-vec", "", position)) {
122 addKeyIfExisting(options, newoptions, "-dfs", "", position);
123 addKeyIfExisting(options, newoptions, "-vls", "", position);
124 addKeyIfExisting(options, newoptions, "-fun", "", position);
125 addKeyIfExisting(options, newoptions, "-g", "", position);
126 addKeyValueIfExisting(options, newoptions, "-vs", "32");
127 addKeyValueIfExisting(options, newoptions, "-lv", "0");
128 } else {
129 addKeyIfExisting(options, newoptions, "-scal", "-scal", position);
130 addKeyIfExisting(options, newoptions, "-inpl", "", position);
131 }
132
133 addKeyValueIfExisting(options, newoptions, "-mcd", "16");
134 addKeyValueIfExisting(options, newoptions, "-cn", "");
135 addKeyValueIfExisting(options, newoptions, "-ftz", "0");
136
137 //------STEP4 - Add other types of Faust options
138 /*
139 addKeyIfExisting(options, newoptions, "-tg", "", position);
140 addKeyIfExisting(options, newoptions, "-sg", "", position);
141 addKeyIfExisting(options, newoptions, "-ps", "", position);
142 addKeyIfExisting(options, newoptions, "-svg", "", position);
143
144 if (addKeyIfExisting(options, newoptions, "-mdoc", "", position)) {
145 addKeyValueIfExisting(options, newoptions, "-mdlang", "");
146 addKeyValueIfExisting(options, newoptions, "-stripdoc", "");
147 }
148
149 addKeyIfExisting(options, newoptions, "-sd", "", position);
150 addKeyValueIfExisting(options, newoptions, "-f", "25");
151 addKeyValueIfExisting(options, newoptions, "-mns", "40");
152 addKeyIfExisting(options, newoptions, "-sn", "", position);
153 addKeyIfExisting(options, newoptions, "-xml", "", position);
154 addKeyIfExisting(options, newoptions, "-blur", "", position);
155 addKeyIfExisting(options, newoptions, "-lb", "", position);
156 addKeyIfExisting(options, newoptions, "-mb", "", position);
157 addKeyIfExisting(options, newoptions, "-rb", "", position);
158 addKeyIfExisting(options, newoptions, "-lt", "", position);
159 addKeyValueIfExisting(options, newoptions, "-a", "");
160 addKeyIfExisting(options, newoptions, "-i", "", position);
161 addKeyValueIfExisting(options, newoptions, "-cn", "");
162 addKeyValueIfExisting(options, newoptions, "-t", "120");
163 addKeyIfExisting(options, newoptions, "-time", "", position);
164 addKeyValueIfExisting(options, newoptions, "-o", "");
165 addKeyValueIfExisting(options, newoptions, "-lang", "cpp");
166 addKeyIfExisting(options, newoptions, "-flist", "", position);
167 addKeyValueIfExisting(options, newoptions, "-l", "");
168 addKeyValueIfExisting(options, newoptions, "-O", "");
169 */
170
171 //-------Add Other Options that are possibily passed to the compiler (-I, -blabla, ...)
172 while (options.size() != 0) {
173 if (options[0] != "faust") newoptions.push_back(options[0]); // "faust" first argument
174 options.erase(options.begin());
175 }
176
177 return newoptions;
178 }
179
extractCompilationOptions(const string & dsp_content)180 static string extractCompilationOptions(const string& dsp_content)
181 {
182 size_t pos1 = dsp_content.find(COMPILATION_OPTIONS_KEY);
183
184 if (pos1 != string::npos) {
185 size_t pos2 = dsp_content.find_first_of('"', pos1 + 1);
186 size_t pos3 = dsp_content.find_first_of('"', pos2 + 1);
187 if (pos2 != string::npos && pos3 != string::npos) {
188 return dsp_content.substr(pos2, (pos3 - pos2) + 1);
189 }
190 }
191
192 return "";
193 }
194
reorganizeCompilationOptions(int argc,const char * argv[])195 string reorganizeCompilationOptions(int argc, const char* argv[])
196 {
197 vector<string> res1;
198 for (int i = 0; i < argc; i++) {
199 res1.push_back(argv[i]);
200 }
201
202 vector<string> res2 = reorganizeCompilationOptionsAux(res1);
203
204 string sep, res3;
205 for (size_t i = 0; i < res2.size(); i++) {
206 res3 = res3 + sep + res2[i];
207 sep = " ";
208 }
209
210 return quote(res3);
211 }
212
sha1FromDSP(const string & name_app,const string & dsp_content,int argc,const char * argv[],string & sha_key)213 string sha1FromDSP(const string& name_app, const string& dsp_content, int argc, const char* argv[], string& sha_key)
214 {
215 sha_key = generateSHA1(name_app + dsp_content + reorganizeCompilationOptions(argc, argv));
216 return dsp_content;
217 }
218
219 // External C++ libfaust API
220
expandDSPFromFile(const string & filename,int argc,const char * argv[],string & sha_key,string & error_msg)221 EXPORT string expandDSPFromFile(const string& filename, int argc, const char* argv[], string& sha_key,
222 string& error_msg)
223 {
224 string base = basename((char*)filename.c_str());
225 size_t pos = filename.find(".dsp");
226 return expandDSPFromString(base.substr(0, pos), pathToContent(filename), argc, argv, sha_key, error_msg);
227 }
228
229 /*
230 Same DSP code and same normalized compilation options will generate the same SHA key.
231 */
expandDSPFromString(const string & name_app,const string & dsp_content,int argc,const char * argv[],string & sha_key,string & error_msg)232 EXPORT string expandDSPFromString(const string& name_app, const string& dsp_content, int argc, const char* argv[],
233 string& sha_key, string& error_msg)
234 {
235 LOCK_API
236 if (dsp_content == "") {
237 error_msg = "Unable to read file";
238 return "";
239 // Already expanded version ?
240 } else if (startWith(dsp_content, COMPILATION_OPTIONS)) {
241 if (extractCompilationOptions(dsp_content) == reorganizeCompilationOptions(argc, argv)) {
242 // Same compilation options as the ones kept in the expanded version
243 sha_key = generateSHA1(dsp_content);
244 return dsp_content;
245 } else {
246 // Otherwise add a new compilation options line, consider it as the new expanded code,
247 // generate SHA key and return it
248 string new_dsp_content =
249 COMPILATION_OPTIONS + reorganizeCompilationOptions(argc, argv) + ";\n" + dsp_content;
250 sha_key = generateSHA1(new_dsp_content);
251 return new_dsp_content;
252 }
253 } else {
254 int argc1 = 0;
255 const char* argv1[64];
256 argv1[argc1++] = "faust";
257 for (int i = 0; i < argc; i++) {
258 argv1[argc1++] = argv[i];
259 }
260 argv1[argc1] = nullptr; // NULL terminated argv
261
262 // 'expandDsp' adds the normalized compilation options in the DSP code before computing the SHA key
263 return expandDSP(argc1, argv1, name_app.c_str(), dsp_content.c_str(), sha_key, error_msg);
264 }
265 }
266
generateAuxFilesFromFile(const string & filename,int argc,const char * argv[],string & error_msg)267 EXPORT bool generateAuxFilesFromFile(const string& filename, int argc, const char* argv[], string& error_msg)
268 {
269 string base = basename((char*)filename.c_str());
270 size_t pos = filename.find(".dsp");
271 return generateAuxFilesFromString(base.substr(0, pos), pathToContent(filename), argc, argv, error_msg);
272 }
273
generateAuxFilesFromString(const string & name_app,const string & dsp_content,int argc,const char * argv[],string & error_msg)274 EXPORT bool generateAuxFilesFromString(const string& name_app, const string& dsp_content, int argc, const char* argv[],
275 string& error_msg)
276 {
277 LOCK_API
278 if (dsp_content == "") {
279 error_msg = "Unable to read file";
280 return false;
281 } else {
282 int argc1 = 0;
283 const char* argv1[64];
284 argv1[argc1++] = "faust";
285 // Filter arguments
286 for (int i = 0; i < argc; i++) {
287 if (!(strcmp(argv[i], "-vec") == 0 || strcmp(argv[i], "-sch") == 0)) {
288 argv1[argc1++] = argv[i];
289 }
290 }
291 argv1[argc1] = nullptr; // NULL terminated argv
292
293 dsp_factory_base* factory = createFactory(name_app.c_str(), dsp_content.c_str(), argc1, argv1, error_msg, false);
294 // Factory is no more needed
295 delete factory;
296 return (factory != nullptr);
297 }
298 }
299
300 // External C libfaust API
301
302 #ifdef __cplusplus
303 extern "C" {
304 #endif
305
expandCDSPFromFile(const char * filename,int argc,const char * argv[],char * sha_key,char * error_msg)306 EXPORT const char* expandCDSPFromFile(const char* filename, int argc, const char* argv[], char* sha_key,
307 char* error_msg)
308 {
309 string sha_key_aux;
310 string error_msg_aux;
311 string res = expandDSPFromFile(filename, argc, argv, sha_key_aux, error_msg_aux);
312 strncpy(sha_key, sha_key_aux.c_str(), 64);
313 strncpy(error_msg, error_msg_aux.c_str(), 4096);
314 return strdup(res.c_str());
315 }
316
expandCDSPFromString(const char * name_app,const char * dsp_content,int argc,const char * argv[],char * sha_key,char * error_msg)317 EXPORT const char* expandCDSPFromString(const char* name_app, const char* dsp_content, int argc, const char* argv[],
318 char* sha_key, char* error_msg)
319 {
320 string sha_key_aux;
321 string error_msg_aux;
322 string res = expandDSPFromString(name_app, dsp_content, argc, argv, sha_key_aux, error_msg_aux);
323 strncpy(sha_key, sha_key_aux.c_str(), 64);
324 strncpy(error_msg, error_msg_aux.c_str(), 4096);
325 return strdup(res.c_str());
326 }
327
generateCAuxFilesFromFile(const char * filename,int argc,const char * argv[],char * error_msg)328 EXPORT bool generateCAuxFilesFromFile(const char* filename, int argc, const char* argv[], char* error_msg)
329 {
330 string error_msg_aux;
331 bool res = generateAuxFilesFromFile(filename, argc, argv, error_msg_aux);
332 strncpy(error_msg, error_msg_aux.c_str(), 4096);
333 return res;
334 }
335
generateCAuxFilesFromString(const char * name_app,const char * dsp_content,int argc,const char * argv[],char * error_msg)336 EXPORT bool generateCAuxFilesFromString(const char* name_app, const char* dsp_content, int argc, const char* argv[],
337 char* error_msg)
338 {
339 string error_msg_aux;
340 bool res = generateAuxFilesFromString(name_app, dsp_content, argc, argv, error_msg_aux);
341 strncpy(error_msg, error_msg_aux.c_str(), 4096);
342 return res;
343 }
344
freeCMemory(void * ptr)345 EXPORT void freeCMemory(void* ptr)
346 {
347 free(ptr);
348 }
349
350 #ifdef __cplusplus
351 }
352 #endif
353