Bug fixed for unix error "readlink /proc/self/fd/0" on MacOS.
[Faustine.git] / interpreter / preprocessor / faust-0.9.47mr3 / compiler / parser / enrobage.cpp
1 /************************************************************************
2 ************************************************************************
3 FAUST compiler
4 Copyright (C) 2003-2004 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
23
24 #include "enrobage.hh"
25 #include <vector>
26 #include <string>
27 #include <limits.h>
28 #include <stdlib.h>
29 #include "compatibility.hh"
30 #include <climits>
31
32 extern string gFaustSuperSuperDirectory;
33 extern string gFaustSuperDirectory;
34 extern string gFaustDirectory;
35 extern string gMasterDirectory;
36 extern string gClassName;
37
38 //----------------------------------------------------------------
39
40
41 /**
42 * Returns true is a line is blank (contains only white caracters)
43 */
44 static bool isBlank(const string& s) {
45 for (size_t i=0; i<s.size(); i++) {
46 if (s[i] != ' ' && s[i] != '\t') return false;
47 }
48 return true;
49 }
50
51
52 /**
53 * Replace every occurrence of oldstr by newstr inside str. str is modified
54 * and returned as reference for convenience
55 */
56 static string& replaceOccurences(string& str, const string& oldstr, const string& newstr)
57 {
58 string::size_type l1 = oldstr.length();
59 string::size_type l2 = newstr.length();
60
61 string::size_type pos = str.find(oldstr);
62 while ( pos != string::npos) {
63 str.replace(pos, l1, newstr);
64 pos = str.find(oldstr, pos + l2);
65 }
66 return str;
67 }
68
69
70 /**
71 * Used when copying architecture files to replace default mydsp
72 * class name with the user specified one
73 */
74 static string& replaceClassName(string& str)
75 {
76 return replaceOccurences(str, "mydsp", gClassName);
77 }
78
79
80 /**
81 * Copy or remove license header. Architecture files can contain a header specifying
82 * the license. If this header contains an exception tag (for example "FAUST COMPILER EXCEPTION")
83 * it is an indication for the compiler to remove the license header from the resulting code.
84 * A header is the first non blank line that begins a comment.
85 */
86 void streamCopyLicense(istream& src, ostream& dst, const string& exceptiontag)
87 {
88 string s;
89 vector<string> H;
90
91 // skip blank lines
92 while (getline(src,s) && isBlank(s)) dst << s << endl;
93
94 // first non blank should start a comment
95 if (s.find("/*")==string::npos) { dst << s << endl; return; }
96
97 // copy the header into H
98 bool remove = false;
99 H.push_back(s);
100
101 while (getline(src,s) && s.find("*/")==string::npos) {
102 H.push_back(s);
103 if (s.find(exceptiontag) != string::npos) remove=true;
104 }
105
106 // copy the header unless explicitely granted to remove it
107 if (!remove) {
108 // copy the header
109 for (unsigned int i=0; i<H.size(); i++) {
110 dst << H[i] << endl;
111 }
112 dst << s << endl;
113 }
114 }
115
116
117 /**
118 * Copy src to dst until specific line.
119 */
120 void streamCopyUntil(istream& src, ostream& dst, const string& until)
121 {
122 string s;
123 while ( getline(src,s) && (s != until) ) dst << replaceClassName(s) << endl;
124 }
125
126 /**
127 * Copy src to dst.
128 */
129 void streamCopy(istream& src, ostream& dst)
130 {
131 string s;
132 while ( getline(src,s)) dst << replaceClassName(s) << endl;
133 }
134
135 /**
136 * Copy src to dst until end
137 */
138 void streamCopyUntilEnd(istream& src, ostream& dst)
139 {
140 string s;
141 while ( getline(src,s) ) dst << replaceClassName(s) << endl;
142 }
143
144
145 /**
146 * Try to open an architecture file searching in various directories
147 */
148 ifstream* open_arch_stream(const char* filename)
149 {
150 char buffer[FAUST_PATH_MAX];
151 char* old = getcwd (buffer, FAUST_PATH_MAX);
152 int err;
153
154 {
155 ifstream* f = new ifstream();
156 f->open(filename, ifstream::in); if (f->is_open()) return f; else delete f;
157 }
158 char *envpath = getenv("FAUST_LIB_PATH");
159 if (envpath!=NULL) {
160 if (chdir(envpath)==0) {
161 ifstream* f = new ifstream();
162 f->open(filename, ifstream::in);
163 if (f->is_open()) return f; else delete f;
164 }
165 }
166 err = chdir(old);
167 if ( (chdir(gFaustDirectory.c_str())==0) && (chdir("architecture")==0) ) {
168 //cout << "enrobage.cpp : 'architecture' directory found in gFaustDirectory" << endl;
169 ifstream* f = new ifstream();
170 f->open(filename, ifstream::in);
171 if (f->good()) return f; else delete f;
172 }
173 err = chdir(old);
174 if ((chdir(gFaustSuperDirectory.c_str())==0) && (chdir("architecture")==0) ) {
175 //cout << "enrobage.cpp : 'architecture' directory found in gFaustSuperDirectory" << endl;
176 ifstream* f = new ifstream();
177 f->open(filename, ifstream::in);
178 if (f->good()) return f; else delete f;
179 }
180 err = chdir(old);
181 if ((chdir(gFaustSuperSuperDirectory.c_str())==0) && (chdir("architecture")==0) ) {
182 //cout << "enrobage.cpp : 'architecture' directory found in gFaustSuperSuperDirectory" << endl;
183 ifstream* f = new ifstream();
184 f->open(filename, ifstream::in);
185 if (f->good()) return f; else delete f;
186 }
187
188 /* Faustine directories (Karim Barkati 09/2013) */
189
190 #ifdef INSTALL_PREFIX
191 err = chdir(old);
192 if (chdir(INSTALL_PREFIX "/lib/faustine")==0) {
193 ifstream* f = new ifstream();
194 f->open(filename);
195 if (f->good()) return f; else delete f;
196 }
197 #endif
198 err = chdir(old);
199 if (chdir("/usr/local/lib/faustine")==0) {
200 ifstream* f = new ifstream();
201 f->open(filename);
202 if (f->good()) return f; else delete f;
203 }
204 err = chdir(old);
205 if (chdir("/usr/lib/faustine")==0) {
206 ifstream* f = new ifstream();
207 f->open(filename);
208 if (f->good()) return f; else delete f;
209 }
210
211 /* End of Faustine directories */
212
213
214 #ifdef INSTALL_PREFIX
215 err = chdir(old);
216 if (chdir(INSTALL_PREFIX "/lib/faust")==0) {
217 ifstream* f = new ifstream();
218 f->open(filename);
219 if (f->good()) return f; else delete f;
220 }
221 #endif
222 err = chdir(old);
223 if (chdir("/usr/local/lib/faust")==0) {
224 ifstream* f = new ifstream();
225 f->open(filename);
226 if (f->good()) return f; else delete f;
227 }
228 err = chdir(old);
229 if (chdir("/usr/lib/faust")==0) {
230 ifstream* f = new ifstream();
231 f->open(filename);
232 if (f->good()) return f; else delete f;
233 }
234
235 return 0;
236 }
237
238
239
240 /*---------------------------------------------*/
241
242 /**
243 * Check if a file exists.
244 * @return true if the file exist, false otherwise
245 */
246
247 bool check_file(const char* filename)
248 {
249 FILE* f = fopen(filename, "r");
250
251 if (f == NULL) {
252 fprintf(stderr, "faust: "); perror(filename);
253 } else {
254 fclose(f);
255 }
256 return f != NULL;
257 }
258
259
260 /**
261 * Try to open the file '<dir>/<filename>'. If it succeed, it stores the full pathname
262 * of the file into <fullpath>
263 */
264 static FILE* fopenat(string& fullpath, const char* dir, const char* filename)
265 {
266 int err;
267 char olddirbuffer[FAUST_PATH_MAX];
268 char newdirbuffer[FAUST_PATH_MAX];
269
270 char* olddir = getcwd (olddirbuffer, FAUST_PATH_MAX);
271
272 if (chdir(dir) == 0) {
273 FILE* f = fopen(filename, "r");
274 fullpath = getcwd (newdirbuffer, FAUST_PATH_MAX);
275 fullpath += '/';
276 fullpath += filename;
277 err = chdir(olddir);
278 return f;
279 }
280 err = chdir(olddir);
281 return 0;
282 }
283
284 /**
285 * Try to open the file '<dir>/<filename>'. If it succeed, it stores the full pathname
286 * of the file into <fullpath>
287 */
288 static FILE* fopenat(string& fullpath, const string& dir, const char* filename)
289 {
290 return fopenat(fullpath, dir.c_str(), filename);
291 }
292
293 /**
294 * Try to open the file '<dir>/<path>/<filename>'. If it succeed, it stores the full pathname
295 * of the file into <fullpath>
296 */
297 static FILE* fopenat(string& fullpath, const string& dir, const char* path, const char* filename)
298 {
299 int err;
300 char olddirbuffer[FAUST_PATH_MAX];
301 char newdirbuffer[FAUST_PATH_MAX];
302
303 char* olddir = getcwd (olddirbuffer, FAUST_PATH_MAX);
304 if (chdir(dir.c_str()) == 0) {
305 if (chdir(path) == 0) {
306 FILE* f = fopen(filename, "r");
307 fullpath = getcwd (newdirbuffer, FAUST_PATH_MAX);
308 fullpath += '/';
309 fullpath += filename;
310 err = chdir(olddir);
311 return f;
312 }
313 }
314 err = chdir(olddir);
315 return 0;
316 }
317
318
319
320 /**
321 * Test absolute pathname.
322 */
323 static bool isAbsolutePathname(const string& filename)
324 {
325 //test windows absolute pathname "x:xxxxxx"
326 if (filename.size()>1 && filename[1] == ':') return true;
327
328 // test unix absolute pathname "/xxxxxx"
329 if (filename.size()>0 && filename[0] == '/') return true;
330
331 return false;
332 }
333
334
335 /**
336 * Build a full pathname of <filename>.
337 * <fullpath> = <currentdir>/<filename>
338 */
339 static void buildFullPathname(string& fullpath, const char* filename)
340 {
341 char old[FAUST_PATH_MAX];
342
343 if (isAbsolutePathname(filename)) {
344 fullpath = filename;
345 } else {
346 fullpath = getcwd (old, FAUST_PATH_MAX);
347 fullpath += '/';
348 fullpath += filename;
349 }
350 }
351
352 /**
353 * Try to open the file <filename> searching in various directories. If succesful
354 * place its full pathname in the string <fullpath>
355 */
356
357 #ifdef WIN32
358 FILE* fopensearch(const char* filename, string& fullpath)
359 {
360 FILE* f;
361 char* envpath;
362
363 if ((f = fopen(filename, "r"))) {
364 buildFullPathname(fullpath, filename);
365 return f;
366 }
367 if ((f = fopenat(fullpath, gMasterDirectory, filename))) {
368 return f;
369 }
370 if ((envpath = getenv("FAUST_LIB_PATH")) && (f = fopenat(fullpath, envpath, filename))) {
371 return f;
372 }
373 if ((f = fopenat(fullpath, gFaustDirectory, "architecture", filename))) {
374 return f;
375 }
376 if ((f = fopenat(fullpath, gFaustSuperDirectory, "architecture", filename))) {
377 return f;
378 }
379 if ((f = fopenat(fullpath, gFaustSuperSuperDirectory, "architecture", filename))) {
380 return f;
381 }
382 return 0;
383 }
384 #else
385 FILE* fopensearch(const char* filename, string& fullpath)
386 {
387 FILE* f;
388 char* envpath;
389
390 if ((f = fopen(filename, "r"))) {
391 buildFullPathname(fullpath, filename);
392 return f;
393 }
394 if ((f = fopenat(fullpath, gMasterDirectory, filename))) {
395 return f;
396 }
397 if ((envpath = getenv("FAUST_LIB_PATH")) && (f = fopenat(fullpath, envpath, filename))) {
398 return f;
399 }
400 if ((f = fopenat(fullpath, gFaustDirectory, "architecture", filename))) {
401 return f;
402 }
403 if ((f = fopenat(fullpath, gFaustSuperDirectory, "architecture", filename))) {
404 return f;
405 }
406 if ((f = fopenat(fullpath, gFaustSuperSuperDirectory, "architecture", filename))) {
407 return f;
408 }
409
410 /* Faustine directories (Haisheng WANG 09/2013) */
411
412 #ifdef INSTALL_PREFIX
413 if ((f = fopenat(fullpath, INSTALL_PREFIX "/lib/faustine", filename))) {
414 return f;
415 }
416 #endif
417 if ((f = fopenat(fullpath, "/usr/local/lib/faustine", filename))) {
418 return f;
419 }
420 if ((f = fopenat(fullpath, "/usr/lib/faustine", filename))) {
421 return f;
422 }
423
424 /* End of Faustine directories */
425
426 #ifdef INSTALL_PREFIX
427 if ((f = fopenat(fullpath, INSTALL_PREFIX "/lib/faust", filename))) {
428 return f;
429 }
430 #endif
431 if ((f = fopenat(fullpath, "/usr/local/lib/faust", filename))) {
432 return f;
433 }
434 if ((f = fopenat(fullpath, "/usr/lib/faust", filename))) {
435 return f;
436 }
437 return 0;
438 }
439 #endif
440
441
442 /**
443 * filebasename returns the basename of a path.
444 * (adapted by kb from basename.c)
445 *
446 * @param[in] The path to parse.
447 * @return The last component of the given path.
448 */
449 #ifndef DIR_SEPARATOR
450 #define DIR_SEPARATOR '/'
451 #endif
452
453 #ifdef WIN32
454 #define HAVE_DOS_BASED_FILE_SYSTEM
455 #ifndef DIR_SEPARATOR_2
456 #define DIR_SEPARATOR_2 '\\'
457 #endif
458 #endif
459
460 /* Define IS_DIR_SEPARATOR. */
461 #ifndef DIR_SEPARATOR_2
462 # define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR)
463 #else /* DIR_SEPARATOR_2 */
464 # define IS_DIR_SEPARATOR(ch) \
465 (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2))
466 #endif /* DIR_SEPARATOR_2 */
467
468
469 /**
470 * returns a pointer on the basename part of name
471 */
472 const char* filebasename(const char* name)
473 {
474 #if defined (HAVE_DOS_BASED_FILE_SYSTEM)
475 /* Skip over the disk name in MSDOS pathnames. */
476 if (isalpha(name[0]) && name[1] == ':')
477 name += 2;
478 #endif
479
480 const char* base;
481 for (base = name; *name; name++)
482 {
483 if (IS_DIR_SEPARATOR (*name))
484 {
485 base = name + 1;
486 }
487 }
488 return base;
489 }
490
491
492 /**
493 * returns a string containing the dirname of name
494 * If no dirname, returns "."
495 */
496 string filedirname(const string& name)
497 {
498 const char* base = filebasename(name.c_str());
499 const unsigned int size = base-name.c_str();
500 string dirname;
501
502 if (size==0) {
503 dirname += '.';
504
505 } else if (size==1) {
506 dirname += name[0];
507
508 } else {
509 for (unsigned int i=0; i<size-1; i++) {
510 dirname += name[i];
511 }
512 }
513 return dirname;
514 }