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