1 /* WhySynth DSSI software synthesizer plugin and GUI
2  *
3  * Copyright (C) 2004-2006, 2010 Sean Bolton and others.
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of
8  * the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be
11  * useful, but WITHOUT ANY WARRANTY; without even the implied
12  * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
13  * PURPOSE.  See the GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this program; if not, write to the Free
17  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301 USA.
19  */
20 
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <unistd.h>
27 #include <stdarg.h>
28 #include <inttypes.h>
29 #include <locale.h>
30 
31 #include "whysynth_types.h"
32 #include "whysynth.h"
33 #include "whysynth_voice.h"
34 
35 y_patch_t y_init_voice = {
36     "  <-->",
37     "",
38     /* -PORTS- */
39     { 1, 0, 0, 0.0f, 0, 0.0f, 0.0f, 0.0f, 0, 0.0f, 0, 0.0f, 0.5f, 0.5f },  /* osc1 */
40     { 0, 0, 0, 0.0f, 0, 0.0f, 0.0f, 0.0f, 0, 0.0f, 0, 0.0f, 0.5f, 0.5f },  /* osc2 */
41     { 0, 0, 0, 0.0f, 0, 0.0f, 0.0f, 0.0f, 0, 0.0f, 0, 0.0f, 0.5f, 0.5f },  /* osc3 */
42     { 0, 0, 0, 0.0f, 0, 0.0f, 0.0f, 0.0f, 0, 0.0f, 0, 0.0f, 0.5f, 0.5f },  /* osc4 */
43     { 1, 0, 50.0f, 0, 0.0f, 0.0f, 0.0f },                                  /* vcf1 */
44     { 0, 0, 50.0f, 0, 0.0f, 0.0f, 0.0f },                                  /* vcf2 */
45     0.0f, 0.2f, 0.0f, 0.8f, 0.5f, 0.5f, 0.5f, 0.5f,                        /* mix */
46     0.5f,                                                                  /* volume */
47     0, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,                           /* effects */
48     0.984375f, 2,                                                          /* glide / bend */
49     { 1.0f, 0, 0.0f, 0, 0.0f },                                            /* glfo */
50     { 1.0f, 0, 0.0f, 0, 0.0f },                                            /* vlfo */
51     { 1.0f, 0, 0.0f, 0, 0.0f }, 90.0f, 0.0f,                               /* mlfo */
52     { 1, 3, 0.004, 1, 3, 0.001, 1, 3, 0.001, 1, 3, 0.2, 0, 0, 0, 0, 0 },   /* ego */
53     { 0, 3, 0.1, 1, 3, 0.1, 1, 3, 0.1, 1, 3, 0.2, 0, 0, 0, 0, 0 },         /* eg1 */
54     { 0, 3, 0.1, 1, 3, 0.1, 1, 3, 0.1, 1, 3, 0.2, 0, 0, 0, 0, 0 },         /* eg2 */
55     { 0, 3, 0.1, 1, 3, 0.1, 1, 3, 0.1, 1, 3, 0.2, 0, 0, 0, 0, 0 },         /* eg3 */
56     { 0, 3, 0.1, 1, 3, 0.1, 1, 3, 0.1, 1, 3, 0.2, 0, 0, 0, 0, 0 },         /* eg4 */
57     1.0f, 0, 0.0f, 0, 0.0f                                                 /* modmix */
58 };
59 
60 int
y_data_is_comment(char * buf)61 y_data_is_comment(char *buf)  /* line is blank, whitespace, or first non-whitespace character is '#' */
62 {
63     int i = 0;
64 
65     while (buf[i]) {
66         if (buf[i] == '#') return 1;
67         if (buf[i] == '\n') return 1;
68         if (buf[i] != ' ' && buf[i] != '\t') return 0;
69         i++;
70     }
71     return 1;
72 }
73 
74 void
y_data_parse_text(const char * buf,char * name,int maxlen)75 y_data_parse_text(const char *buf, char *name, int maxlen)
76 {
77     int i = 0, o = 0;
78     unsigned int t;
79 
80     while (buf[i] && o < maxlen) {
81         if (buf[i] < 33 || buf[i] > 126) {
82             break;
83         } else if (buf[i] == '%') {
84             if (buf[i + 1] && buf[i + 2] && sscanf(buf + i + 1, "%2x", &t) == 1) {
85                 name[o++] = (char)t;
86                 i += 3;
87             } else {
88                 break;
89             }
90         } else {
91             name[o++] = buf[i++];
92         }
93     }
94     /* trim trailing spaces */
95     while (o && name[o - 1] == ' ') o--;
96     name[o] = '\0';
97 }
98 
99 /* y_sscanf.c - dual-locale sscanf
100  *
101  * Like the standard sscanf(3), but this version accepts floating point
102  * numbers containing either the locale-specified decimal point or the
103  * "C" locale decimal point (".").  That said, there are important
104  * differences between this and a standard sscanf(3):
105  *
106  *  - this function only skips whitespace when there is explicit ' ' in
107  *      the format,
108  *  - it doesn't return EOF like sscanf(3), just the number of sucessful
109  *      conversions,
110  *  - it doesn't match character classes,
111  *  - there is only one length modifier: 'l', and it only applies to 'd'
112  *      (for int64_t) and 'f' (for double),
113  *  - '%s' with no field-width specified is an error,
114  *  - there are no 'i, 'o', 'p', or 'u' conversions, similarly
115  *  - use 'f' instead of 'a', 'A', 'e', 'E', 'F', 'g', or 'G', and
116  *  - '%f' doesn't do hexadecimal, infinity or NaN.
117  */
118 
119 static int
_is_whitespace(char c)120 _is_whitespace(char c)
121 {
122     return (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v');
123 }
124 
125 static int
_is_digit(char c)126 _is_digit(char c)
127 {
128     return (c >= '0' && c <= '9');
129 }
130 
131 static int
_is_hexdigit(char c)132 _is_hexdigit(char c)
133 {
134     return ((c >= '0' && c <= '9') ||
135             (c >= 'a' && c <= 'f') ||
136             (c >= 'A' && c <= 'F'));
137 }
138 
139 static int y_atof(const char *buffer, double *result); /* forward */
140 
141 static int
y_vsscanf(const char * buffer,const char * format,va_list ap)142 y_vsscanf(const char *buffer, const char *format, va_list ap)
143 {
144     char fc;
145     const char *bp = buffer;
146     int conversions = 0;
147 
148     while ((fc = *format++)) {
149 
150         if (_is_whitespace(fc)) { /* skip whitespace */
151 
152             while (_is_whitespace(*bp))
153                 bp++;
154 
155         } else if (fc == '%') { /* a conversion */
156 
157             int skip = 0;   /* '*' no-store modifier */
158             int width = 0;  /* field width */
159             int big = 0;    /* storage length modifier */
160 
161             if (*format == '*') {
162                 skip = 1;
163                 format++;
164             }
165             while (_is_digit(*format)) {
166                 width = width * 10 + (*format - '0');
167                 format++;
168             }
169             if (*format == 'l') {
170                 big = 1;
171                 format++;
172             }
173 
174             if (*format == '%') {  /* '%' - literal percent character */
175 
176                 if (*bp == '%')
177                     bp++;
178                 else
179                     break;
180                 format++;
181 
182             } else if (*format == 'c') {  /* 'c' - one or more characters */
183 
184                 int i;
185                 char *sp = va_arg(ap, char *);
186 
187                 if (width == 0) width = 1;
188                 for (i = 0; i < width && *bp != 0; i++, bp++)
189                     if (!skip) *sp++ = *bp;
190                 if (i > 0 && !skip)
191                     conversions++;
192                 format++;
193 
194             } else if (*format == 'd') {  /* 'd' - 32 or 64 bit signed decimal integer */
195 
196                 int negative = 0;
197                 int i;
198                 int64_t n = 0;
199 
200                 if (*bp == '-') {
201                     negative = 1;
202                     bp++;
203                 }
204                 for (i = negative; (width == 0 || i < width) && _is_digit(*bp); i++, bp++)
205                     n = n * 10 + (*bp - '0');
206                 if (i == negative) /* no digits converted */
207                     break;
208                 if (negative) n = -n;
209                 if (!skip) {
210                     if (big)
211                         *va_arg(ap, int64_t *) = n;
212                     else
213                         *va_arg(ap, int32_t *) = n;
214                     conversions++;
215                 }
216                 format++;
217 
218             } else if (*format == 'f') {  /* 'f' - float or double */
219 
220                 double d;
221                 int n = y_atof(bp, &d);
222                 if (n == 0)  /* no digits converted */
223                     break;
224                 if (!skip) {
225                     if (big)
226                         *va_arg(ap, double *) = d;
227                     else
228                         *va_arg(ap, float *) = (float)d;
229                     conversions++;
230                 }
231                 bp += n;
232                 format++;
233 
234             } else if (*format == 'n') {  /* 'n' - store number of characters scanned so far */
235 
236                 if (!skip) *va_arg(ap, int *) = bp - buffer;
237                 format++;
238 
239             } else if (*format == 's') {  /* 's' - string of non-whitespace characters */
240 
241                 int i;
242                 char *sp = va_arg(ap, char *);
243                 if (width == 0)
244                     break; /* must specify a width */
245                 for (i = 0; i < width && *bp != 0 && !_is_whitespace(*bp); i++, bp++)
246                     if (!skip) *sp++ = *bp;
247                 if (i > 0) {
248                     if (!skip) {
249                         *sp = 0;
250                         conversions++;
251                     }
252                 } else
253                     break;  /* conversion failed */
254                 format++;
255 
256             } else if (*format == 'x') {  /* 'x' - 32 or 64 bit signed hexidecimal integer */
257                 int i;
258                 int64_t n = 0;
259 
260                 for (i = 0; (width == 0 || i < width) && _is_hexdigit(*bp); i++, bp++) {
261                     n = n * 16;
262                     if (*bp >= 'a') n += *bp - 'a' + 10;
263                     else if (*bp >= 'A') n += *bp - 'A' + 10;
264                     else n += *bp - '0';
265                 }
266                 if (i == 0) /* no digits converted */
267                     break;
268                 if (!skip) {
269                     if (big)
270                         *va_arg(ap, int64_t *) = n;
271                     else
272                         *va_arg(ap, int32_t *) = n;
273                     conversions++;
274                 }
275                 format++;
276 
277             } else {
278                 break; /* bad conversion character */
279             }
280 
281         } else if (fc == *bp) { /* a literal match */
282 
283             bp++;
284 
285         } else {  /* match fail */
286             break;
287         }
288     }
289 
290     return conversions;
291 }
292 
293 /* The following function is based on sqlite3AtoF() from sqlite 3.6.18.
294  * The sqlite author disclaims copyright to the source code from which
295  * this was adapted. */
296 static int
y_atof(const char * z,double * pResult)297 y_atof(const char *z, double *pResult){
298   const char *zBegin = z;
299   /* sign * significand * (10 ^ (esign * exponent)) */
300   int sign = 1;   /* sign of significand */
301   int64_t s = 0;  /* significand */
302   int d = 0;      /* adjust exponent for shifting decimal point */
303   int esign = 1;  /* sign of exponent */
304   int e = 0;      /* exponent */
305   double result;
306   int nDigits = 0;
307   struct lconv *lc = localeconv();
308   int dplen = strlen(lc->decimal_point);
309 
310   /* skip leading spaces */
311   /* while( _is_whitespace(*z) ) z++; */
312   /* get sign of significand */
313   if( *z=='-' ){
314     sign = -1;
315     z++;
316   }else if( *z=='+' ){
317     z++;
318   }
319   /* skip leading zeroes */
320   while( z[0]=='0' ) z++, nDigits++;
321 
322   /* copy max significant digits to significand */
323   while( _is_digit(*z) && s<((INT64_MAX-9)/10) ){
324     s = s*10 + (*z - '0');
325     z++, nDigits++;
326   }
327   /* skip non-significant significand digits
328   ** (increase exponent by d to shift decimal left) */
329   while( _is_digit(*z) ) z++, nDigits++, d++;
330 
331   /* if decimal point is present */
332   if( *z=='.' || !strncmp(z, lc->decimal_point, dplen) ) {
333     if (*z=='.')
334       z++;
335     else
336       z += dplen;
337     /* copy digits from after decimal to significand
338     ** (decrease exponent by d to shift decimal right) */
339     while( _is_digit(*z) && s<((INT64_MAX-9)/10) ){
340       s = s*10 + (*z - '0');
341       z++, nDigits++, d--;
342     }
343     /* skip non-significant digits */
344     while( _is_digit(*z) ) z++, nDigits++;
345   } else if (nDigits == 0)
346       return 0;  /* no significand digits converted */
347 
348   /* if exponent is present */
349   if( *z=='e' || *z=='E' ){
350     int eDigits = 0;
351     z++;
352     /* get sign of exponent */
353     if( *z=='-' ){
354       esign = -1;
355       z++;
356     }else if( *z=='+' ){
357       z++;
358     }
359     /* copy digits to exponent */
360     while( _is_digit(*z) ){
361       e = e*10 + (*z - '0');
362       z++, eDigits++;
363     }
364     if (eDigits == 0)
365         return 0; /* malformed exponent */
366   }
367 
368   /* adjust exponent by d, and update sign */
369   e = (e*esign) + d;
370   if( e<0 ) {
371     esign = -1;
372     e *= -1;
373   } else {
374     esign = 1;
375   }
376 
377   /* if 0 significand */
378   if( !s ) {
379     /* In the IEEE 754 standard, zero is signed.
380     ** Add the sign if we've seen at least one digit */
381     result = (sign<0 && nDigits) ? -(double)0 : (double)0;
382   } else {
383     /* attempt to reduce exponent */
384     if( esign>0 ){
385       while( s<(INT64_MAX/10) && e>0 ) e--,s*=10;
386     }else{
387       while( !(s%10) && e>0 ) e--,s/=10;
388     }
389 
390     /* adjust the sign of significand */
391     s = sign<0 ? -s : s;
392 
393     /* if exponent, scale significand as appropriate
394     ** and store in result. */
395     if( e ){
396       double scale = 1.0;
397       /* attempt to handle extremely small/large numbers better */
398       if( e>307 && e<342 ){
399         while( e%308 ) { scale *= 1.0e+1; e -= 1; }
400         if( esign<0 ){
401           result = s / scale;
402           result /= 1.0e+308;
403         }else{
404           result = s * scale;
405           result *= 1.0e+308;
406         }
407       }else{
408         /* 1.0e+22 is the largest power of 10 than can be
409         ** represented exactly. */
410         while( e%22 ) { scale *= 1.0e+1; e -= 1; }
411         while( e>0 ) { scale *= 1.0e+22; e -= 22; }
412         if( esign<0 ){
413           result = s / scale;
414         }else{
415           result = s * scale;
416         }
417       }
418     } else {
419       result = (double)s;
420     }
421   }
422 
423   /* store the result */
424   *pResult = result;
425 
426   /* return number of characters used */
427   return (int)(z - zBegin);
428 }
429 
430 int
y_sscanf(const char * str,const char * format,...)431 y_sscanf(const char *str, const char *format, ...)
432 {
433     va_list ap;
434     int conversions;
435 
436     va_start(ap, format);
437     conversions = y_vsscanf(str, format, ap);
438     va_end(ap);
439 
440     return conversions;
441 }
442 
443 /* end of y_sscanf.c */
444 
445 int
y_data_read_patch(FILE * file,y_patch_t * patch)446 y_data_read_patch(FILE *file, y_patch_t *patch)
447 {
448     int i;
449     char c, buf[256], buf2[180];
450     y_patch_t tmp;
451 
452     do {
453         if (!fgets(buf, 256, file)) return 0;
454     } while (y_data_is_comment(buf));
455 
456     if (sscanf(buf, " WhySynth patch format %d begin", &i) != 1 ||
457         i != 0)
458         return 0;
459 
460     memcpy(&tmp, &y_init_voice, sizeof(y_patch_t));
461 
462     while (1) {
463 
464         if (!fgets(buf, 256, file)) return 0;
465 
466         /* 'name %20%20<init%20voice>' */
467         if (sscanf(buf, " name %90s", buf2) == 1) {
468 
469             y_data_parse_text(buf2, tmp.name, 30);
470             continue;
471 
472         /* 'comment %20%20<init%20voice>' */
473         } else if (sscanf(buf, " comment %180s", buf2) == 1) {
474 
475             y_data_parse_text(buf2, tmp.comment, 60);
476             continue;
477 
478         /* -PORTS- */
479         /* 'oscY 1 1 0 0 0 0 0 0 0.5 0 0 0 0 0.5 0.5' */
480         /* 'oscY 2 0 0 0 0 0 0 0 0.5 0 0 0 0 0.5 0.5' */
481         /* 'oscY 3 0 0 0 0 0 0 0 0.5 0 0 0 0 0.5 0.5' */
482         /* 'oscY 4 0 0 0 0 0 0 0 0.5 0 0 0 0 0.5 0.5' */
483         } else if (sscanf(buf, " oscY %d", &i) == 1) {
484 
485             struct posc *osc;
486 
487             switch (i) {
488               case 1: osc = &tmp.osc1; break;
489               case 2: osc = &tmp.osc2; break;
490               case 3: osc = &tmp.osc3; break;
491               case 4: osc = &tmp.osc4; break;
492               default:
493                 return 0;
494             }
495             if (y_sscanf(buf, " oscY %d %d %d %d %f %d %f %f %f %d %f %d %f %f %f",
496                          &i, &osc->mode, &osc->waveform, &osc->pitch,
497                          &osc->detune, &osc->pitch_mod_src, &osc->pitch_mod_amt,
498                          &osc->mparam1, &osc->mparam2, &osc->mmod_src,
499                          &osc->mmod_amt, &osc->amp_mod_src, &osc->amp_mod_amt,
500                          &osc->level_a, &osc->level_b) != 15)
501                 return 0;
502 
503             continue;
504 
505         /* 'vcfY 1 1 0 50 0 0 0 0' */
506         /* 'vcfY 2 0 0 50 0 0 0 0' */
507         } else if (sscanf(buf, " vcfY %d", &i) == 1) {
508 
509             struct pvcf *vcf;
510 
511             switch (i) {
512               case 1: vcf = &tmp.vcf1; break;
513               case 2: vcf = &tmp.vcf2; break;
514               default:
515                 return 0;
516             }
517             if (y_sscanf(buf, " vcfY %d %d %d %f %d %f %f %f",
518                          &i, &vcf->mode, &vcf->source, &vcf->frequency,
519                          &vcf->freq_mod_src, &vcf->freq_mod_amt, &vcf->qres,
520                          &vcf->mparam) != 8)
521                 return 0;
522 
523             continue;
524 
525         /* 'mix 0 0.2 0 0.8 0.5 0.5 0.5 0.5' */
526         } else if (y_sscanf(buf, " mix %f %f %f %f %f %f %f %f",
527                             &tmp.busa_level, &tmp.busa_pan,
528                             &tmp.busb_level, &tmp.busb_pan,
529                             &tmp.vcf1_level, &tmp.vcf1_pan,
530                             &tmp.vcf2_level, &tmp.vcf2_pan) == 8) {
531 
532             continue;
533 
534         /* 'volume 0.5' */
535         } else if (y_sscanf(buf, " volume %f", &tmp.volume) == 1) {
536 
537             continue;
538 
539         /* 'effects 0 0 0 0 0' */
540         } else if (y_sscanf(buf, " effects %d %f %f %f %f %f %f %f",
541                             &tmp.effect_mode, &tmp.effect_param1,
542                             &tmp.effect_param2, &tmp.effect_param3,
543                             &tmp.effect_param4, &tmp.effect_param5,
544                             &tmp.effect_param6, &tmp.effect_mix) == 8) {
545 
546             continue;
547 
548         /* 'glide 0.984375' */
549         } else if (y_sscanf(buf, " glide %f", &tmp.glide_time) == 1) {
550 
551             continue;
552 
553         /* 'bend 2' */
554         } else if (sscanf(buf, " bend %d", &tmp.bend_range) == 1) {
555 
556             continue;
557 
558         /* 'lfoY g 1 0 0 0 0' */
559         /* 'lfoY v 1 0 0 0 0' */
560         /* 'lfoY m 1 0 0 0 0' */
561         } else if (sscanf(buf, " lfoY %c", &c) == 1) {
562 
563             struct plfo *lfo;
564 
565             switch (c) {
566               case 'g': lfo = &tmp.glfo; break;
567               case 'v': lfo = &tmp.vlfo; break;
568               case 'm': lfo = &tmp.mlfo; break;
569               default:
570                 return 0;
571             }
572             if (y_sscanf(buf, " lfoY %c %f %d %f %d %f",
573                          &c, &lfo->frequency, &lfo->waveform, &lfo->delay,
574                          &lfo->amp_mod_src, &lfo->amp_mod_amt) != 6)
575                 return 0;
576 
577         /* 'mlfo 90 0' */
578         } else if (y_sscanf(buf, " mlfo %f %f", &tmp.mlfo_phase_spread,
579                             &tmp.mlfo_random_freq) == 2) {
580 
581             continue;
582 
583         /* 'egY o 0 0.1 1 0.1 1 0.1 1 0.2 0.1 1 0 0 0 0 0' */
584         /* 'egY 1 0 0.1 1 0.1 1 0.1 1 0.2 0.1 1 0 0 0 0 0' */
585         /* 'egY 2 0 0.1 1 0.1 1 0.1 1 0.2 0.1 1 0 0 0 0 0' */
586         /* 'egY 3 0 0.1 1 0.1 1 0.1 1 0.2 0.1 1 0 0 0 0 0' */
587         /* 'egY 4 0 0.1 1 0.1 1 0.1 1 0.2 0.1 1 0 0 0 0 0' */
588         } else if (sscanf(buf, " egY %c", &c) == 1) {
589 
590             struct peg *eg;
591 
592             switch (c) {
593               case 'o': eg = &tmp.ego; break;
594               case '1': eg = &tmp.eg1; break;
595               case '2': eg = &tmp.eg2; break;
596               case '3': eg = &tmp.eg3; break;
597               case '4': eg = &tmp.eg4; break;
598               default:
599                 return 0;
600             }
601             if (y_sscanf(buf, " egY %c %d %d %f %f %d %f %f %d %f %f %d %f %f %f %f %d %f",
602                          &c, &eg->mode,
603                          &eg->shape1, &eg->time1, &eg->level1,
604                          &eg->shape2, &eg->time2, &eg->level2,
605                          &eg->shape3, &eg->time3, &eg->level3,
606                          &eg->shape4, &eg->time4,
607                          &eg->vel_level_sens, &eg->vel_time_scale,
608                          &eg->kbd_time_scale, &eg->amp_mod_src,
609                          &eg->amp_mod_amt) != 18)
610                 return 0;
611 
612         /* 'modmix 1 0 0 0 0' */
613         } else if (y_sscanf(buf, " modmix %f %d %f %d %f", &tmp.modmix_bias,
614                             &tmp.modmix_mod1_src, &tmp.modmix_mod1_amt,
615                             &tmp.modmix_mod2_src, &tmp.modmix_mod2_amt) == 5) {
616 
617             continue;
618 
619         /* 'WhySynth patch end' */
620         } else if (sscanf(buf, " WhySynth patch %3s", buf2) == 1 &&
621                  !strcmp(buf2, "end")) {
622 
623             break; /* finished */
624 
625         } else {
626 
627             return 0; /* unrecognized line */
628         }
629     }
630 
631     memcpy(patch, &tmp, sizeof(y_patch_t));
632 
633     return 1;  /* -FIX- error handling yet to be implemented */
634 }
635 
636 char *
y_data_locate_patch_file(const char * origpath,const char * project_dir)637 y_data_locate_patch_file(const char *origpath, const char *project_dir)
638 {
639     struct stat statbuf;
640     char *path;
641     const char *filename;
642 
643     if (stat(origpath, &statbuf) == 0)
644         return strdup(origpath);
645     else if (!project_dir)
646         return NULL;
647 
648     filename = strrchr(origpath, '/');
649 
650     if (filename) ++filename;
651     else filename = origpath;
652     if (!*filename) return NULL;
653 
654     path = (char *)malloc(strlen(project_dir) + strlen(filename) + 2);
655     sprintf(path, "%s/%s", project_dir, filename);
656 
657     if (stat(path, &statbuf) == 0)
658         return path;
659 
660     free(path);
661     return NULL;
662 }
663 
664