next attempt to fix the use of wordexp(3) ...
[ardour.git] / libs / ardour / utils.cc
1 /*
2     Copyright (C) 2000-2003 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #ifdef WAF_BUILD
21 #include "libardour-config.h"
22 #endif
23
24 #include <stdint.h>
25
26 #include <cstdio> /* for sprintf */
27 #include <cstring>
28 #include <cstdlib>
29 #include <cmath>
30 #include <cctype>
31 #include <cstring>
32 #include <cerrno>
33 #include <iostream>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <sys/time.h>
37 #include <fcntl.h>
38 #include <dirent.h>
39 #include <errno.h>
40
41 #include <glibmm/miscutils.h>
42 #include <glibmm/fileutils.h>
43
44 #ifdef HAVE_WORDEXP
45 #include <wordexp.h>
46 #endif
47
48 #include "pbd/cpus.h"
49 #include "pbd/error.h"
50 #include "pbd/stacktrace.h"
51 #include "pbd/xml++.h"
52 #include "pbd/basename.h"
53 #include "pbd/strsplit.h"
54 #include "pbd/replace_all.h"
55
56 #include "ardour/utils.h"
57 #include "ardour/rc_configuration.h"
58
59 #include "i18n.h"
60
61 using namespace ARDOUR;
62 using namespace std;
63 using namespace PBD;
64
65 string
66 legalize_for_path (const string& str)
67 {
68         string::size_type pos;
69         string illegal_chars = "/\\"; /* DOS, POSIX. Yes, we're going to ignore HFS */
70         string legal;
71
72         legal = str;
73         pos = 0;
74
75         while ((pos = legal.find_first_of (illegal_chars, pos)) != string::npos) {
76                 legal.replace (pos, 1, "_");
77                 pos += 1;
78         }
79
80         return string (legal);
81 }
82
83 string
84 bump_name_once (const std::string& name, char delimiter)
85 {
86         string::size_type delim;
87         string newname;
88
89         if ((delim = name.find_last_of (delimiter)) == string::npos) {
90                 newname  = name;
91                 newname += delimiter;
92                 newname += "1";
93         } else {
94                 int isnumber = 1;
95                 const char *last_element = name.c_str() + delim + 1;
96                 for (size_t i = 0; i < strlen(last_element); i++) {
97                         if (!isdigit(last_element[i])) {
98                                 isnumber = 0;
99                                 break;
100                         }
101                 }
102
103                 errno = 0;
104                 int32_t version = strtol (name.c_str()+delim+1, (char **)NULL, 10);
105
106                 if (isnumber == 0 || errno != 0) {
107                         // last_element is not a number, or is too large
108                         newname  = name;
109                         newname  += delimiter;
110                         newname += "1";
111                 } else {
112                         char buf[32];
113
114                         snprintf (buf, sizeof(buf), "%d", version+1);
115
116                         newname  = name.substr (0, delim+1);
117                         newname += buf;
118                 }
119         }
120
121         return newname;
122
123 }
124
125 bool
126 could_be_a_valid_path (const string& path)
127 {
128         vector<string> posix_dirs;
129         vector<string> dos_dirs;
130         string testpath;
131
132         split (path, posix_dirs, '/');
133         split (path, dos_dirs, '\\');
134
135         /* remove the last component of each */
136
137         posix_dirs.erase (--posix_dirs.end());
138         dos_dirs.erase (--dos_dirs.end());
139
140         if (G_DIR_SEPARATOR == '/') {
141                 for (vector<string>::iterator x = posix_dirs.begin(); x != posix_dirs.end(); ++x) {
142                         testpath = Glib::build_filename (testpath, *x);
143                         cerr << "Testing " << testpath << endl;
144                         if (!Glib::file_test (testpath, Glib::FILE_TEST_IS_DIR|Glib::FILE_TEST_EXISTS)) {
145                                 return false;
146                         }
147                 }
148         }
149
150         if (G_DIR_SEPARATOR == '\\') {
151                 testpath = "";
152                 for (vector<string>::iterator x = dos_dirs.begin(); x != dos_dirs.end(); ++x) {
153                         testpath = Glib::build_filename (testpath, *x);
154                         cerr << "Testing " << testpath << endl;
155                         if (!Glib::file_test (testpath, Glib::FILE_TEST_IS_DIR|Glib::FILE_TEST_EXISTS)) {
156                                 return false;
157                         }
158                 }
159         }
160
161         return true;
162 }
163
164
165 XMLNode *
166 find_named_node (const XMLNode& node, string name)
167 {
168         XMLNodeList nlist;
169         XMLNodeConstIterator niter;
170         XMLNode* child;
171
172         nlist = node.children();
173
174         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
175
176                 child = *niter;
177
178                 if (child->name() == name) {
179                         return child;
180                 }
181         }
182
183         return 0;
184 }
185
186 int
187 cmp_nocase (const string& s, const string& s2)
188 {
189         string::const_iterator p = s.begin();
190         string::const_iterator p2 = s2.begin();
191
192         while (p != s.end() && p2 != s2.end()) {
193                 if (toupper(*p) != toupper(*p2)) {
194                         return (toupper(*p) < toupper(*p2)) ? -1 : 1;
195                 }
196                 ++p;
197                 ++p2;
198         }
199
200         return (s2.size() == s.size()) ? 0 : (s.size() < s2.size()) ? -1 : 1;
201 }
202
203 int
204 touch_file (string path)
205 {
206         int fd = open (path.c_str(), O_RDWR|O_CREAT, 0660);
207         if (fd >= 0) {
208                 close (fd);
209                 return 0;
210         }
211         return 1;
212 }
213
214 string
215 region_name_from_path (string path, bool strip_channels, bool add_channel_suffix, uint32_t total, uint32_t this_one)
216 {
217         path = PBD::basename_nosuffix (path);
218
219         if (strip_channels) {
220
221                 /* remove any "?R", "?L" or "?[a-z]" channel identifier */
222
223                 string::size_type len = path.length();
224
225                 if (len > 3 && (path[len-2] == '%' || path[len-2] == '?' || path[len-2] == '.') &&
226                     (path[len-1] == 'R' || path[len-1] == 'L' || (islower (path[len-1])))) {
227
228                         path = path.substr (0, path.length() - 2);
229                 }
230         }
231
232         if (add_channel_suffix) {
233
234                 path += '%';
235
236                 if (total > 2) {
237                         path += (char) ('a' + this_one);
238                 } else {
239                         path += (char) (this_one == 0 ? 'L' : 'R');
240                 }
241         }
242
243         return path;
244 }
245
246 bool
247 path_is_paired (string path, string& pair_base)
248 {
249         string::size_type pos;
250
251         /* remove any leading path */
252
253         if ((pos = path.find_last_of (G_DIR_SEPARATOR)) != string::npos) {
254                 path = path.substr(pos+1);
255         }
256
257         /* remove filename suffixes etc. */
258
259         if ((pos = path.find_last_of ('.')) != string::npos) {
260                 path = path.substr (0, pos);
261         }
262
263         string::size_type len = path.length();
264
265         /* look for possible channel identifier: "?R", "%R", ".L" etc. */
266
267         if (len > 3 && (path[len-2] == '%' || path[len-2] == '?' || path[len-2] == '.') &&
268             (path[len-1] == 'R' || path[len-1] == 'L' || (islower (path[len-1])))) {
269
270                 pair_base = path.substr (0, len-2);
271                 return true;
272
273         }
274
275         return false;
276 }
277
278 string
279 path_expand (string path)
280 {
281         if (path.empty()) {
282                 return path;
283         }
284
285 #ifdef HAVE_WORDEXP
286         /* Handle tilde and environment variable expansion in session path */
287         string ret = path;
288         string quoted;
289         wordexp_t expansion;
290
291         /* wordexp cannot be forced (it appears) into either
292            
293            (1) NOT doing field splitting
294            (2) splitting based on something other than whitespace
295            
296            (despite the documentation claiming that it obeys IFS etc).
297
298            so, quote the most likely spaces to occur in a path, and that should
299            be about as much as we can do.
300         */
301
302         quoted = path;
303         replace_all (quoted, " ", "\\ ");
304
305         switch (wordexp (quoted.c_str(), &expansion, WRDE_NOCMD|WRDE_UNDEF)) {
306         case 0:
307                 break;
308         case WRDE_NOSPACE:
309                 /* see docs on wordexp() */
310                 wordfree (&expansion);
311                 /* fallthru */
312         default:
313                 error << string_compose (_("illegal or badly-formed string used for path (%1)"), path) << endmsg;
314                 goto out;
315         }
316
317         if (expansion.we_wordc > 1) {
318                 string all;
319                 for (unsigned int i = 0; i < expansion.we_wordc; ++i) {
320                         if (i > 0) {
321                                 all += " | ";
322                         } 
323                         all += expansion.we_wordv[i];
324                 }
325                 error << string_compose (_("path (%1) is ambiguous: %2"), path, all) << endmsg;
326                 goto out;
327         }
328
329         ret = expansion.we_wordv[0];
330   out:
331         wordfree (&expansion);
332         return ret;
333
334 #else
335         return path;
336 #endif
337 }
338
339 #if __APPLE__
340 string
341 CFStringRefToStdString(CFStringRef stringRef)
342 {
343         CFIndex size =
344                 CFStringGetMaximumSizeForEncoding(CFStringGetLength(stringRef) ,
345                 kCFStringEncodingUTF8);
346             char *buf = new char[size];
347
348         std::string result;
349
350         if(CFStringGetCString(stringRef, buf, size, kCFStringEncodingUTF8)) {
351             result = buf;
352         }
353         delete [] buf;
354         return result;
355 }
356 #endif // __APPLE__
357
358 void
359 compute_equal_power_fades (framecnt_t nframes, float* in, float* out)
360 {
361         double step;
362
363         step = 1.0/(nframes-1);
364
365         in[0] = 0.0f;
366
367         for (framecnt_t i = 1; i < nframes - 1; ++i) {
368                 in[i] = in[i-1] + step;
369         }
370
371         in[nframes-1] = 1.0;
372
373         const float pan_law_attenuation = -3.0f;
374         const float scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
375
376         for (framecnt_t n = 0; n < nframes; ++n) {
377                 float inVal = in[n];
378                 float outVal = 1 - inVal;
379                 out[n] = outVal * (scale * outVal + 1.0f - scale);
380                 in[n] = inVal * (scale * inVal + 1.0f - scale);
381         }
382 }
383
384 EditMode
385 string_to_edit_mode (string str)
386 {
387         if (str == _("Splice")) {
388                 return Splice;
389         } else if (str == _("Slide")) {
390                 return Slide;
391         } else if (str == _("Lock")) {
392                 return Lock;
393         }
394         fatal << string_compose (_("programming error: unknown edit mode string \"%1\""), str) << endmsg;
395         /*NOTREACHED*/
396         return Slide;
397 }
398
399 const char*
400 edit_mode_to_string (EditMode mode)
401 {
402         switch (mode) {
403         case Slide:
404                 return _("Slide");
405
406         case Lock:
407                 return _("Lock");
408
409         default:
410         case Splice:
411                 return _("Splice");
412         }
413 }
414
415 SyncSource
416 string_to_sync_source (string str)
417 {
418         if (str == _("MIDI Timecode") || str == _("MTC")) {
419                 return MTC;
420         }
421
422         if (str == _("MIDI Clock")) {
423                 return MIDIClock;
424         }
425
426         if (str == _("JACK")) {
427                 return JACK;
428         }
429
430         fatal << string_compose (_("programming error: unknown sync source string \"%1\""), str) << endmsg;
431         /*NOTREACHED*/
432         return JACK;
433 }
434
435 /** @param sh Return a short version of the string */
436 const char*
437 sync_source_to_string (SyncSource src, bool sh)
438 {
439         switch (src) {
440         case JACK:
441                 return _("JACK");
442
443         case MTC:
444                 if (sh) {
445                         return _("MTC");
446                 } else {
447                         return _("MIDI Timecode");
448                 }
449
450         case MIDIClock:
451                 return _("MIDI Clock");
452         }
453         /* GRRRR .... stupid, stupid gcc - you can't get here from there, all enum values are handled */
454         return _("JACK");
455 }
456
457 float
458 meter_falloff_to_float (MeterFalloff falloff)
459 {
460         switch (falloff) {
461         case MeterFalloffOff:
462                 return METER_FALLOFF_OFF;
463         case MeterFalloffSlowest:
464                 return METER_FALLOFF_SLOWEST;
465         case MeterFalloffSlow:
466                 return METER_FALLOFF_SLOW;
467         case MeterFalloffMedium:
468                 return METER_FALLOFF_MEDIUM;
469         case MeterFalloffFast:
470                 return METER_FALLOFF_FAST;
471         case MeterFalloffFaster:
472                 return METER_FALLOFF_FASTER;
473         case MeterFalloffFastest:
474                 return METER_FALLOFF_FASTEST;
475         default:
476                 return METER_FALLOFF_FAST;
477         }
478 }
479
480 MeterFalloff
481 meter_falloff_from_float (float val)
482 {
483         if (val == METER_FALLOFF_OFF) {
484                 return MeterFalloffOff;
485         }
486         else if (val <= METER_FALLOFF_SLOWEST) {
487                 return MeterFalloffSlowest;
488         }
489         else if (val <= METER_FALLOFF_SLOW) {
490                 return MeterFalloffSlow;
491         }
492         else if (val <= METER_FALLOFF_MEDIUM) {
493                 return MeterFalloffMedium;
494         }
495         else if (val <= METER_FALLOFF_FAST) {
496                 return MeterFalloffFast;
497         }
498         else if (val <= METER_FALLOFF_FASTER) {
499                 return MeterFalloffFaster;
500         }
501         else {
502                 return MeterFalloffFastest;
503         }
504 }
505
506 AutoState
507 ARDOUR::string_to_auto_state (std::string str)
508 {
509         if (str == X_("Off")) {
510                 return Off;
511         } else if (str == X_("Play")) {
512                 return Play;
513         } else if (str == X_("Write")) {
514                 return Write;
515         } else if (str == X_("Touch")) {
516                 return Touch;
517         }
518
519         fatal << string_compose (_("programming error: %1 %2"), "illegal AutoState string: ", str) << endmsg;
520         /*NOTREACHED*/
521         return Touch;
522 }
523
524 string
525 ARDOUR::auto_state_to_string (AutoState as)
526 {
527         /* to be used only for XML serialization, no i18n done */
528
529         switch (as) {
530         case Off:
531                 return X_("Off");
532                 break;
533         case Play:
534                 return X_("Play");
535                 break;
536         case Write:
537                 return X_("Write");
538                 break;
539         case Touch:
540                 return X_("Touch");
541         }
542
543         fatal << string_compose (_("programming error: %1 %2"), "illegal AutoState type: ", as) << endmsg;
544         /*NOTREACHED*/
545         return "";
546 }
547
548 AutoStyle
549 ARDOUR::string_to_auto_style (std::string str)
550 {
551         if (str == X_("Absolute")) {
552                 return Absolute;
553         } else if (str == X_("Trim")) {
554                 return Trim;
555         }
556
557         fatal << string_compose (_("programming error: %1 %2"), "illegal AutoStyle string: ", str) << endmsg;
558         /*NOTREACHED*/
559         return Trim;
560 }
561
562 string
563 ARDOUR::auto_style_to_string (AutoStyle as)
564 {
565         /* to be used only for XML serialization, no i18n done */
566
567         switch (as) {
568         case Absolute:
569                 return X_("Absolute");
570                 break;
571         case Trim:
572                 return X_("Trim");
573                 break;
574         }
575
576         fatal << string_compose (_("programming error: %1 %2"), "illegal AutoStyle type: ", as) << endmsg;
577         /*NOTREACHED*/
578         return "";
579 }
580
581 std::string
582 bool_as_string (bool yn)
583 {
584         return (yn ? "yes" : "no");
585 }
586
587 bool
588 string_is_affirmative (const std::string& str)
589 {
590         /* to be used only with XML data - not intended to handle user input */
591
592         if (str.empty ()) {
593                 return false;
594         }
595
596         /* the use of g_strncasecmp() is solely to get around issues with
597          * charsets posed by trying to use C++ for the same
598          * comparison. switching a std::string to its lower- or upper-case
599          * version has several issues, but handled by default
600          * in the way we desire when doing it in C.
601          */
602
603         return str == "1" || str == "y" || str == "Y" || (!g_strncasecmp(str.c_str(), "yes", str.length()));
604 }
605
606 const char*
607 native_header_format_extension (HeaderFormat hf, const DataType& type)
608 {
609         if (type == DataType::MIDI) {
610                 return ".mid";
611         }
612
613         switch (hf) {
614         case BWF:
615                 return ".wav";
616         case WAVE:
617                 return ".wav";
618         case WAVE64:
619                 return ".w64";
620         case CAF:
621                 return ".caf";
622         case AIFF:
623                 return ".aif";
624         case iXML:
625                 return ".ixml";
626         case RF64:
627                 return ".rf64";
628         }
629
630         fatal << string_compose (_("programming error: unknown native header format: %1"), hf);
631         /*NOTREACHED*/
632         return ".wav";
633 }
634
635 bool
636 matching_unsuffixed_filename_exists_in (const string& dir, const string& path)
637 {
638         string bws = basename_nosuffix (path);
639         struct dirent* dentry;
640         struct stat statbuf;
641         DIR* dead;
642         bool ret = false;
643
644         if ((dead = ::opendir (dir.c_str())) == 0) {
645                 error << string_compose (_("cannot open directory %1 (%2)"), dir, strerror (errno)) << endl;
646                 return false;
647         }
648
649         while ((dentry = ::readdir (dead)) != 0) {
650
651                 /* avoid '.' and '..' */
652
653                 if ((dentry->d_name[0] == '.' && dentry->d_name[1] == '\0') ||
654                     (dentry->d_name[2] == '\0' && dentry->d_name[0] == '.' && dentry->d_name[1] == '.')) {
655                         continue;
656                 }
657
658                 string fullpath = Glib::build_filename (dir, dentry->d_name);
659
660                 if (::stat (fullpath.c_str(), &statbuf)) {
661                         continue;
662                 }
663
664                 if (!S_ISREG (statbuf.st_mode)) {
665                         continue;
666                 }
667
668                 string bws2 = basename_nosuffix (dentry->d_name);
669
670                 if (bws2 == bws) {
671                         ret = true;
672                         break;
673                 }
674         }
675
676         ::closedir (dead);
677         return ret;
678 }
679
680 uint32_t
681 how_many_dsp_threads ()
682 {
683         /* CALLER MUST HOLD PROCESS LOCK */
684
685         int num_cpu = hardware_concurrency();
686         int pu = Config->get_processor_usage ();
687         uint32_t num_threads = max (num_cpu - 1, 2); // default to number of cpus minus one, or 2, whichever is larger
688
689         if (pu < 0) {
690                 /* pu is negative: use "pu" less cores for DSP than appear to be available
691                  */
692
693                 if (-pu < num_cpu) {
694                         num_threads = num_cpu + pu;
695                 }
696
697         } else if (pu == 0) {
698
699                 /* use all available CPUs
700                  */
701
702                 num_threads = num_cpu;
703
704         } else {
705                 /* use "pu" cores, if available
706                  */
707
708                 num_threads = min (num_cpu, pu);
709         }
710
711         return num_threads;
712 }
713
714 double gain_to_slider_position_with_max (double g, double max_gain)
715 {
716         return gain_to_slider_position (g * 2.0/max_gain);
717 }
718
719 double slider_position_to_gain_with_max (double g, double max_gain)
720 {
721         return slider_position_to_gain (g * max_gain/2.0);
722 }
723
724 extern "C" {
725         void c_stacktrace() { stacktrace (cerr); }
726 }