Add option to limit automatable control parmaters
[ardour.git] / libs / ardour / find_session.cc
1 /*
2     Copyright (C) 2012 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 #include <unistd.h>
21
22 #include <cstring>
23 #include <climits>
24 #include <cerrno>
25
26 #include "pbd/gstdio_compat.h"
27
28 #include <glibmm/miscutils.h>
29 #include <glibmm/fileutils.h>
30
31 #include "pbd/basename.h"
32 #include "pbd/compose.h"
33 #include "pbd/file_archive.h"
34 #include "pbd/pathexpand.h"
35 #include "pbd/error.h"
36
37 #include "ardour/filename_extensions.h"
38 #include "ardour/utils.h"
39 #include "ardour/session_utils.h"
40
41 #include "pbd/i18n.h"
42
43 using namespace std;
44 using namespace PBD;
45
46 namespace ARDOUR {
47
48 int
49 find_session (string str, string& path, string& snapshot, bool& isnew)
50 {
51         GStatBuf statbuf;
52
53         isnew = false;
54
55         str = canonical_path (str);
56
57         /* check to see if it exists, and what it is */
58
59         if (g_stat (str.c_str(), &statbuf)) {
60                 if (errno == ENOENT) {
61                         isnew = true;
62                 } else {
63                         error << string_compose (_("cannot check session path %1 (%2)"), str, strerror (errno))
64                               << endmsg;
65                         return -1;
66                 }
67         }
68
69         if (!isnew) {
70
71                 /* it exists, so it must either be the name
72                    of the directory, or the name of the statefile
73                    within it.
74                 */
75
76                 if (S_ISDIR (statbuf.st_mode)) {
77
78                         string::size_type slash = str.find_last_of (G_DIR_SEPARATOR);
79
80                         if (slash == string::npos) {
81
82                                 /* a subdirectory of cwd, so statefile should be ... */
83
84                                 string tmp = Glib::build_filename (str, str+statefile_suffix);
85
86                                 /* is it there ? */
87
88                                 if (g_stat (tmp.c_str(), &statbuf)) {
89                                         error << string_compose (_("cannot check statefile %1 (%2)"), tmp, strerror (errno))
90                                               << endmsg;
91                                         return -1;
92                                 }
93
94                                 path = str;
95                                 snapshot = str;
96
97                         } else {
98
99                                 /* some directory someplace in the filesystem.
100                                    the snapshot name is the directory name
101                                    itself.
102                                 */
103
104                                 path = str;
105                                 snapshot = str.substr (slash+1);
106
107                         }
108
109                 } else if (S_ISREG (statbuf.st_mode)) {
110
111                         string::size_type slash = str.find_last_of (G_DIR_SEPARATOR);
112                         string::size_type suffix;
113
114                         /* remove the suffix */
115
116                         if (slash != string::npos) {
117                                 snapshot = str.substr (slash+1);
118                         } else {
119                                 snapshot = str;
120                         }
121
122                         suffix = snapshot.find (statefile_suffix);
123
124                         const string::size_type start_pos_of_extension = snapshot.size () - strlen (statefile_suffix);
125                         // we should check the start of extension position
126                         // because files '*.ardour.bak' are possible
127                         if (suffix != start_pos_of_extension) {
128                                 error << string_compose (_("%1 is not a snapshot file"), str) << endmsg;
129                                 return -1;
130                         }
131
132                         /* remove suffix */
133
134                         snapshot = snapshot.substr (0, suffix);
135
136                         if (slash == string::npos) {
137
138                                 /* we must be in the directory where the
139                                    statefile lives. get it using cwd().
140                                 */
141
142                                 char cwd[PATH_MAX+1];
143
144                                 if (getcwd (cwd, sizeof (cwd)) == 0) {
145                                         error << string_compose (_("cannot determine current working directory (%1)"), strerror (errno))
146                                               << endmsg;
147                                         return -1;
148                                 }
149
150                                 path = cwd;
151
152                         } else {
153
154                                 /* full path to the statefile */
155
156                                 path = str.substr (0, slash);
157                         }
158
159                 } else {
160
161                         /* what type of file is it? */
162                         error << string_compose (_("unknown file type for session %1"), str) << endmsg;
163                         return -1;
164                 }
165
166         } else {
167
168                 /* its the name of a new directory. get the name
169                    as "dirname" does.
170                 */
171
172                 string::size_type slash = str.find_last_of (G_DIR_SEPARATOR);
173
174                 if (slash == string::npos) {
175
176                         /* no slash, just use the name, but clean it up */
177
178                         path = legalize_for_path (str);
179                         snapshot = path;
180
181                 } else {
182
183                         path = str;
184                         snapshot = str.substr (slash+1);
185                 }
186         }
187
188         return 0;
189 }
190
191 /* check if zip is a session-archive,
192  * return > 0 if file is not an archive
193  * return < 0 if unzip failed
194  * return 0 on success.  path and snapshot are set.
195  */
196 int
197 inflate_session (const std::string& zipfile, const std::string& target_dir, string& path, string& snapshot)
198 {
199         if (zipfile.find (session_archive_suffix) == string::npos) {
200                 return 1;
201         }
202
203         try {
204                 PBD::FileArchive ar (zipfile);
205                 std::vector<std::string> files = ar.contents ();
206
207                 if (files.size () == 0) {
208                         error << _("Archive is empty") << endmsg;
209                         return 2;
210                 }
211
212                 /* session archives are expected to be named after the archive */
213                 std::string bn = Glib::path_get_dirname (files.front());
214                 if (bn.empty ()) {
215                         error << _("Archive does not contain a session folder") << endmsg;
216                         return 3;
217                 }
218
219                 size_t sep = bn.find_first_of ('/');
220
221                 if (sep != string::npos) {
222                         bn = bn.substr (0, sep);
223                 }
224
225                 if (bn.empty ()) {
226                         error << _("Archive does not contain a valid session structure") << endmsg;
227                         return 4;
228                 }
229
230                 string sn = Glib::build_filename (bn, bn + statefile_suffix);
231
232                 if (std::find (files.begin(), files.end(), sn) == files.end()) {
233                         error << _("Archive does not contain a session file") << endmsg;
234                         return 5;
235                 }
236
237                 /* check if target folder exists */
238                 string dest = Glib::build_filename (target_dir, bn);
239                 if (Glib::file_test (dest, Glib::FILE_TEST_EXISTS)) {
240                         error << string_compose (_("Destination '%1' already exists."), dest) << endmsg;
241                         return -1;
242                 }
243
244                 if (0 == ar.inflate (target_dir)) {
245                         info << string_compose (_("Extracted session-archive to '%1'."), dest) << endmsg;
246                         path = dest;
247                         snapshot = bn;
248                         return 0;
249                 }
250
251         } catch (...) {
252                 error << _("Error reading file-archive") << endmsg;
253                 return 6;
254         }
255
256         error << _("Error extracting file-archive") << endmsg;
257         return -2;
258 }
259
260 string inflate_error (int e) {
261         switch (e) {
262                 case 0:
263                         return _("No Error");
264                 case 1:
265                         return string_compose (_("File extension is not %1"), session_archive_suffix);
266                 case 2:
267                         return _("Archive is empty");
268                 case 3:
269                         return _("Archive does not contain a session folder");
270                 case 4:
271                         return _("Archive does not contain a valid session structure");
272                 case 5:
273                         return _("Archive does not contain a session file");
274                 case 6:
275                         return _("Error reading file-archive");
276                 case -1:
277                         return _("Destination folder already exists.");
278                 case -2:
279                         return _("Error extracting file-archive");
280                 default:
281                         assert (0);
282                         break;
283         }
284         return _("Unknown Error");
285 }
286
287 }  // namespace ARDOUR