1 /*
2  * Copyright (C) 2005-2016 Paul Davis <paul@linuxaudiosystems.com>
3  * Copyright (C) 2005 Taybin Rutkin <taybin@taybin.com>
4  * Copyright (C) 2006-2012 Tim Mayberry <mojofunk@gmail.com>
5  * Copyright (C) 2008-2012 David Robillard <d@drobilla.net>
6  * Copyright (C) 2015 John Emmas <john@creativepost.co.uk>
7  * Copyright (C) 2015 Robin Gareus <robin@gareus.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program; if not, write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  */
23 
24 #include <cstring>
25 #include <cerrno>
26 #include <sstream>
27 #include <algorithm>
28 
29 #include "pbd/gstdio_compat.h"
30 #include <glibmm/miscutils.h>
31 
32 #include "pbd/error.h"
33 
34 #include "ardour/rc_configuration.h"
35 #include "ardour/filesystem_paths.h"
36 #include "ardour/recent_sessions.h"
37 
38 #include "pbd/i18n.h"
39 
40 using namespace std;
41 using namespace ARDOUR;
42 using namespace PBD;
43 
44 namespace {
45 
46 	const char * const recent_file_name = "recent";
47 	const char * const recent_templates_file_name = "recent_templates";
48 
49 } // anonymous
50 
51 int
read_recent_sessions(RecentSessions & rs)52 ARDOUR::read_recent_sessions (RecentSessions& rs)
53 {
54 	std::string path = Glib::build_filename (user_config_directory(), recent_file_name);
55 	FILE* fin = g_fopen (path.c_str(), "rb");
56 
57 	if (!fin) {
58 		if (errno != ENOENT) {
59 			error << string_compose (_("cannot open recent session file %1 (%2)"), path, strerror (errno)) << endmsg;
60 			return -1;
61 		} else {
62 			return 1;
63 		}
64 	}
65 
66 	// Read the file into a std::string
67 	std::stringstream recent;
68 	while (!feof (fin)) {
69 		char buf[1024];
70 		size_t charsRead = fread (buf, sizeof(char), 1024, fin);
71 		if (ferror (fin)) {
72 			error << string_compose (_("Error reading recent session file %1 (%2)"), path, strerror (errno)) << endmsg;
73 			fclose(fin);
74 			return -1;
75 		}
76 		if (charsRead == 0) {
77 			break;
78 		}
79 		recent.write (buf, charsRead);
80 	}
81 
82 	while (true) {
83 
84 		pair<string,string> newpair;
85 
86 		getline(recent, newpair.first);
87 
88 		if (!recent.good()) {
89 			break;
90 		}
91 
92 		getline(recent, newpair.second);
93 
94 		if (!recent.good()) {
95 			break;
96 		}
97 
98 		rs.push_back (newpair);
99 	}
100 
101 	/* display sorting should be done in the GUI, otherwise the
102 	 * natural order will be broken
103 	 */
104 
105 	fclose (fin);
106 	return 0;
107 }
108 
109 int
read_recent_templates(std::deque<std::string> & rt)110 ARDOUR::read_recent_templates (std::deque<std::string>& rt)
111 {
112 	std::string path = Glib::build_filename (user_config_directory(), recent_templates_file_name);
113 	FILE* fin = g_fopen (path.c_str(), "rb");
114 
115 	if (!fin) {
116 		if (errno != ENOENT) {
117 			error << string_compose (_("Cannot open recent template file %1 (%2)"), path, strerror (errno)) << endmsg;
118 			return -1;
119 		} else {
120 			return 1;
121 		}
122 	}
123 
124 	// Copy the file contents into a std::stringstream
125 	std::stringstream recent;
126 	while (!feof (fin)) {
127 		char buf[1024];
128 		size_t charsRead = fread (buf, sizeof(char), 1024, fin);
129 		if (ferror (fin)) {
130 			error << string_compose (_("Error reading recent session file %1 (%2)"), path, strerror (errno)) << endmsg;
131 			fclose(fin);
132 			return -1;
133 		}
134 		if (charsRead == 0) {
135 			break;
136 		}
137 		recent.write (buf, charsRead);
138 	}
139 
140 	while (true) {
141 
142 		std::string session_template_full_name;
143 
144 		getline(recent, session_template_full_name);
145 
146 		if (!recent.good()) {
147 			break;
148 		}
149 
150 		rt.push_back (session_template_full_name);
151 	}
152 
153 	fclose (fin);
154 	return 0;
155 }
156 
157 int
write_recent_sessions(RecentSessions & rs)158 ARDOUR::write_recent_sessions (RecentSessions& rs)
159 {
160 	FILE* fout = g_fopen (Glib::build_filename (user_config_directory(), recent_file_name).c_str(), "wb");
161 
162 	if (!fout) {
163 		return -1;
164 	}
165 
166 	{
167 		stringstream recent;
168 
169 		for (RecentSessions::iterator i = rs.begin(); i != rs.end(); ++i) {
170 			recent << (*i).first << '\n' << (*i).second << endl;
171 		}
172 
173 		string recentString = recent.str();
174 		size_t writeSize = recentString.length();
175 
176 		fwrite(recentString.c_str(), sizeof(char), writeSize, fout);
177 
178 		if (ferror(fout))
179 		{
180 			error << string_compose (_("Error writing recent sessions file %1 (%2)"), recent_file_name, strerror (errno)) << endmsg;
181 			fclose(fout);
182 			return -1;
183 		}
184 	}
185 
186 
187 
188 	fclose (fout);
189 
190 	return 0;
191 }
192 
193 int
write_recent_templates(std::deque<std::string> & rt)194 ARDOUR::write_recent_templates (std::deque<std::string>& rt)
195 {
196 	FILE* fout = g_fopen (Glib::build_filename (user_config_directory(), recent_templates_file_name).c_str(), "wb");
197 
198 	if (!fout) {
199 		return -1;
200 	}
201 
202 	stringstream recent;
203 
204 	for (std::deque<std::string>::const_iterator i = rt.begin(); i != rt.end(); ++i) {
205 		recent << (*i) << std::endl;
206 	}
207 
208 	string recentString = recent.str();
209 	size_t writeSize = recentString.length();
210 
211 	fwrite(recentString.c_str(), sizeof(char), writeSize, fout);
212 
213 	if (ferror(fout))
214 	{
215 		error << string_compose (_("Error writing saved template file %1 (%2)"), recent_templates_file_name, strerror (errno)) << endmsg;
216 		fclose(fout);
217 		return -1;
218 	}
219 
220 	fclose (fout);
221 
222 	return 0;
223 }
224 
225 int
store_recent_sessions(string name,string path)226 ARDOUR::store_recent_sessions (string name, string path)
227 {
228 	RecentSessions rs;
229 
230 	if (ARDOUR::read_recent_sessions (rs) < 0) {
231 		return -1;
232 	}
233 
234 	pair<string,string> newpair;
235 
236 	newpair.first = name;
237 	newpair.second = path;
238 
239 	rs.erase(remove(rs.begin(), rs.end(), newpair), rs.end());
240 
241 	rs.push_front (newpair);
242 
243 	uint32_t max_recent_sessions = Config->get_max_recent_sessions();
244 
245 	if (rs.size() > max_recent_sessions) {
246 		rs.erase(rs.begin()+max_recent_sessions, rs.end());
247 	}
248 
249 	return ARDOUR::write_recent_sessions (rs);
250 }
251 
252 int
store_recent_templates(const std::string & session_template_full_name)253 ARDOUR::store_recent_templates (const std::string& session_template_full_name)
254 {
255 	std::deque<std::string> rt;
256 
257 	if (ARDOUR::read_recent_templates (rt) < 0) {
258 		return -1;
259 	}
260 
261 	rt.erase(remove (rt.begin(), rt.end(), session_template_full_name), rt.end());
262 
263 	rt.push_front (session_template_full_name);
264 
265 	uint32_t max_recent_templates = Config->get_max_recent_templates ();
266 
267 	if (rt.size() > max_recent_templates) {
268 		rt.erase( rt.begin() + max_recent_templates, rt.end ());
269 	}
270 
271 	return ARDOUR::write_recent_templates (rt);
272 }
273 
274 int
remove_recent_sessions(const string & path)275 ARDOUR::remove_recent_sessions (const string& path)
276 {
277 	RecentSessions rs;
278 	bool write = false;
279 
280 	if (ARDOUR::read_recent_sessions (rs) < 0) {
281 		return -1;
282 	}
283 
284 	for (RecentSessions::iterator i = rs.begin(); i != rs.end(); ++i) {
285 		if (i->second == path) {
286 			rs.erase (i);
287 			write = true;
288 			break;
289 		}
290 	}
291 
292 	if (write) {
293 		return ARDOUR::write_recent_sessions (rs);
294 	} else {
295 		return 1;
296 	}
297 }
298