/[svn]/linuxsampler/trunk/src/db/InstrumentsDb.cpp
ViewVC logotype

Contents of /linuxsampler/trunk/src/db/InstrumentsDb.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1345 - (show annotations) (download)
Thu Sep 13 21:46:25 2007 UTC (16 years, 7 months ago) by iliev
File size: 65083 byte(s)
* added support for escape sequences to the instruments database

1 /***************************************************************************
2 * *
3 * Copyright (C) 2007 Grigor Iliev *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the Free Software *
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, *
18 * MA 02110-1301 USA *
19 ***************************************************************************/
20
21 #include "InstrumentsDb.h"
22
23 #if HAVE_SQLITE3
24
25 #include <iostream>
26 #include <sstream>
27 #include <vector>
28 #include <dirent.h>
29 #include <errno.h>
30 #include <fnmatch.h>
31
32 #include "../common/Exception.h"
33
34 namespace LinuxSampler {
35
36 InstrumentsDb* InstrumentsDb::pInstrumentsDb = new InstrumentsDb;
37
38 void InstrumentsDb::CreateInstrumentsDb(String File) {
39 struct stat statBuf;
40 int res = stat(File.c_str(), &statBuf);
41 if (!res) {
42 throw Exception("File exists: " + File);
43 }
44
45 GetInstrumentsDb()->SetDbFile(File);
46
47 String sql =
48 " CREATE TABLE instr_dirs ( "
49 " dir_id INTEGER PRIMARY KEY AUTOINCREMENT, "
50 " parent_dir_id INTEGER DEFAULT 0, "
51 " dir_name TEXT, "
52 " created TIMESTAMP DEFAULT CURRENT_TIMESTAMP, "
53 " modified TIMESTAMP DEFAULT CURRENT_TIMESTAMP, "
54 " description TEXT, "
55 " FOREIGN KEY(parent_dir_id) REFERENCES instr_dirs(dir_id), "
56 " UNIQUE (parent_dir_id,dir_name) "
57 " ); ";
58
59 GetInstrumentsDb()->ExecSql(sql);
60
61 sql = "INSERT INTO instr_dirs (dir_id, parent_dir_id, dir_name) VALUES (0, 0, '/');";
62 GetInstrumentsDb()->ExecSql(sql);
63
64 sql =
65 " CREATE TABLE instruments ( "
66 " instr_id INTEGER PRIMARY KEY AUTOINCREMENT, "
67 " dir_id INTEGER DEFAULT 0, "
68 " instr_name TEXT, "
69 " instr_file TEXT, "
70 " instr_nr INTEGER, "
71 " format_family TEXT, "
72 " format_version TEXT, "
73 " instr_size INTEGER, "
74 " created TIMESTAMP DEFAULT CURRENT_TIMESTAMP, "
75 " modified TIMESTAMP DEFAULT CURRENT_TIMESTAMP, "
76 " description TEXT, "
77 " is_drum INTEGER(1), "
78 " product TEXT, "
79 " artists TEXT, "
80 " keywords TEXT, "
81 " FOREIGN KEY(dir_id) REFERENCES instr_dirs(dir_id), "
82 " UNIQUE (dir_id,instr_name) "
83 " ); ";
84
85 GetInstrumentsDb()->ExecSql(sql);
86 }
87
88 InstrumentsDb::InstrumentsDb() {
89 db = NULL;
90 DbInstrumentsMutex = Mutex();
91 InTransaction = false;
92 }
93
94 InstrumentsDb::~InstrumentsDb() {
95 if (db != NULL) sqlite3_close(db);
96 }
97
98 void InstrumentsDb::Destroy() {
99 if (pInstrumentsDb != NULL) {
100 delete pInstrumentsDb;
101 pInstrumentsDb = NULL;
102 }
103 }
104
105 void InstrumentsDb::AddInstrumentsDbListener(InstrumentsDb::Listener* l) {
106 llInstrumentsDbListeners.AddListener(l);
107 }
108
109 void InstrumentsDb::RemoveInstrumentsDbListener(InstrumentsDb::Listener* l) {
110 llInstrumentsDbListeners.RemoveListener(l);
111 }
112
113 InstrumentsDb* InstrumentsDb::GetInstrumentsDb() {
114 return pInstrumentsDb;
115 }
116
117 void InstrumentsDb::SetDbFile(String File) {
118 DbInstrumentsMutex.Lock();
119 if (File.empty() || DbFile.length() > 0) {
120 DbInstrumentsMutex.Unlock();
121 throw Exception("Failed to set the database file");
122 }
123 DbFile = File;
124 DbInstrumentsMutex.Unlock();
125 }
126
127 sqlite3* InstrumentsDb::GetDb() {
128 if ( db != NULL) return db;
129
130 if (DbFile.empty()) DbFile = "/var/lib/linuxsampler/instruments.db";
131 int rc = sqlite3_open(DbFile.c_str(), &db);
132 if (rc) {
133 sqlite3_close(db);
134 db = NULL;
135 throw Exception("Cannot open instruments database: " + DbFile);
136 }
137 rc = sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8, NULL, Regexp, NULL, NULL);
138 if (rc) { throw Exception("Failed to add user function for handling regular expressions."); }
139
140 return db;
141 }
142
143 int InstrumentsDb::GetDirectoryCount(int DirId) {
144 dmsg(2,("InstrumentsDb: GetDirectoryCount(DirId=%d)\n", DirId));
145 if(DirId == -1) return -1;
146
147 std::stringstream sql;
148 sql << "SELECT COUNT(*) FROM instr_dirs WHERE parent_dir_id=" << DirId;
149
150 int count = ExecSqlInt(sql.str());
151
152 // While the root dir has ID 0 and parent ID 0, the directory
153 // count for the root dir will be incorrect, so we should fix it.
154 if (count != -1 && DirId == 0) count--;
155 return count;
156 }
157
158 int InstrumentsDb::GetDirectoryCount(String Dir, bool Recursive) {
159 dmsg(2,("InstrumentsDb: GetDirectoryCount(Dir=%s,Recursive=%d)\n", Dir.c_str(), Recursive));
160 int i;
161
162 BeginTransaction();
163 try {
164 if (Recursive) {
165 DirectoryCounter directoryCounter;
166 DirectoryTreeWalk(Dir, &directoryCounter);
167 i = directoryCounter.GetDirectoryCount();
168 } else {
169 i = GetDirectoryCount(GetDirectoryId(Dir));
170 }
171 } catch (Exception e) {
172 EndTransaction();
173 throw e;
174 }
175 EndTransaction();
176 if (i == -1) throw Exception("Unkown DB directory: " + toEscapedPath(Dir));
177
178 return i;
179 }
180
181 IntListPtr InstrumentsDb::GetDirectoryIDs(int DirId) {
182 std::stringstream sql;
183 sql << "SELECT dir_id FROM instr_dirs ";
184 sql << "WHERE parent_dir_id=" << DirId << " AND dir_id!=0";
185
186 return ExecSqlIntList(sql.str());
187 }
188
189 StringListPtr InstrumentsDb::GetDirectories(String Dir, bool Recursive) {
190 dmsg(2,("InstrumentsDb: GetDirectories(Dir=%s,Recursive=%d)\n", Dir.c_str(), Recursive));
191
192 BeginTransaction();
193 try {
194 int dirId = GetDirectoryId(Dir);
195 if(dirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
196
197 StringListPtr pDirs;
198 if (Recursive) {
199 SearchQuery q;
200 DirectoryFinder directoryFinder(&q);
201 DirectoryTreeWalk(Dir, &directoryFinder);
202 pDirs = directoryFinder.GetDirectories();
203 } else {
204 pDirs = GetDirectories(dirId);
205 }
206 EndTransaction();
207 return pDirs;
208 } catch (Exception e) {
209 EndTransaction();
210 throw e;
211 }
212 }
213
214 StringListPtr InstrumentsDb::GetDirectories(int DirId) {
215 std::stringstream sql;
216 sql << "SELECT dir_name FROM instr_dirs ";
217 sql << "WHERE parent_dir_id=" << DirId << " AND dir_id!=0";
218 StringListPtr dirs = ExecSqlStringList(sql.str());
219
220 for (int i = 0; i < dirs->size(); i++) {
221 for (int j = 0; j < dirs->at(i).length(); j++) {
222 if (dirs->at(i).at(j) == '/') dirs->at(i).at(j) = '\0';
223 }
224 }
225
226 return dirs;
227 }
228
229 int InstrumentsDb::GetDirectoryId(String Dir) {
230 dmsg(2,("InstrumentsDb: GetDirectoryId(Dir=%s)\n", Dir.c_str()));
231 CheckPathName(Dir);
232
233 if (Dir.empty() || Dir.at(0) != '/') {
234 return -1;
235 } else if (Dir.length() == 1) {
236 // We expect the root directory id to be always 0.
237 return 0;
238 }
239
240 int id = 0, i = 1;
241 int j = Dir.find('/', i);
242
243 while(j != std::string::npos) {
244 id = GetDirectoryId(id, Dir.substr(i, j - i));
245 i = j + 1;
246 if (i >= Dir.length()) return id;
247 j = Dir.find('/', i);
248 }
249
250 return GetDirectoryId(id, Dir.substr(i));
251 }
252
253 int InstrumentsDb::GetDirectoryId(int ParentDirId, String DirName) {
254 dmsg(2,("InstrumentsDb: GetDirectoryId(ParentDirId=%d, DirName=%s)\n", ParentDirId, DirName.c_str()));
255 DirName = toDbName(DirName);
256 std::stringstream sql;
257 sql << "SELECT dir_id FROM instr_dirs WHERE parent_dir_id=";
258 sql << ParentDirId << " AND dir_name=?";
259 return ExecSqlInt(sql.str(), DirName);
260 }
261
262 String InstrumentsDb::GetDirectoryName(int DirId) {
263 String sql = "SELECT dir_name FROM instr_dirs WHERE dir_id=" + ToString(DirId);
264 String name = ExecSqlString(sql);
265 if (name.empty()) throw Exception("Directory ID not found");
266 return name;
267 }
268
269 int InstrumentsDb::GetParentDirectoryId(int DirId) {
270 if (DirId == 0) throw Exception("The root directory is specified");
271 String sql = "SELECT parent_dir_id FROM instr_dirs WHERE dir_id=" + ToString(DirId);
272 int parentId = ExecSqlInt(sql);
273 if (parentId == -1) throw Exception("DB directory not found");
274 return parentId;
275 }
276
277 String InstrumentsDb::GetDirectoryPath(int DirId) {
278 String path = "";
279 int count = 1000; // used to prevent infinite loops
280
281 while(--count) {
282 if (DirId == 0) {
283 path = "/" + path;
284 break;
285 }
286 path = GetDirectoryName(DirId) + path;
287 DirId = GetParentDirectoryId(DirId);
288 }
289
290 if (!count) throw Exception("Possible infinite loop detected");
291
292 return path;
293 }
294
295 void InstrumentsDb::AddDirectory(String Dir) {
296 dmsg(2,("InstrumentsDb: AddDirectory(Dir=%s)\n", Dir.c_str()));
297 CheckPathName(Dir);
298 String ParentDir = GetParentDirectory(Dir);
299
300 BeginTransaction();
301 try {
302 if (Dir.length() > 1) {
303 if (Dir.at(Dir.length() - 1) == '/') Dir.erase(Dir.length() - 1);
304 }
305
306 String dirName = GetFileName(Dir);
307 if(ParentDir.empty() || dirName.empty()) {
308 throw Exception("Failed to add DB directory: " + toEscapedPath(Dir));
309 }
310
311 int id = GetDirectoryId(ParentDir);
312 if (id == -1) throw Exception("DB directory doesn't exist: " + toEscapedPath(ParentDir));
313 int id2 = GetDirectoryId(id, dirName);
314 if (id2 != -1) throw Exception("DB directory already exist: " + toEscapedPath(Dir));
315 id2 = GetInstrumentId(id, dirName);
316 if (id2 != -1) throw Exception("Instrument with that name exist: " + toEscapedPath(Dir));
317
318 std::stringstream sql;
319 sql << "INSERT INTO instr_dirs (parent_dir_id, dir_name) VALUES (";
320 sql << id << ", ?)";
321
322 ExecSql(sql.str(), toDbName(dirName));
323 } catch (Exception e) {
324 EndTransaction();
325 throw e;
326 }
327
328 EndTransaction();
329
330 FireDirectoryCountChanged(ParentDir);
331 }
332
333 void InstrumentsDb::RemoveDirectory(String Dir, bool Force) {
334 dmsg(2,("InstrumentsDb: RemoveDirectory(Dir=%s,Force=%d)\n", Dir.c_str(), Force));
335
336 String ParentDir = GetParentDirectory(Dir);
337
338 BeginTransaction();
339 try {
340 int dirId = GetDirectoryId(Dir);
341 if (dirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
342 if (dirId == 0) throw Exception("Cannot delete the root directory: " + Dir);
343 if(ParentDir.empty()) throw Exception("Unknown parent directory");
344 if (Force) RemoveDirectoryContent(dirId);
345 RemoveDirectory(dirId);
346 } catch (Exception e) {
347 EndTransaction();
348 throw e;
349 }
350
351 EndTransaction();
352 FireDirectoryCountChanged(ParentDir);
353 }
354
355 void InstrumentsDb::RemoveDirectoryContent(int DirId, int Level) {
356 dmsg(2,("InstrumentsDb: RemoveDirectoryContent(DirId=%d,Level=%d)\n", DirId, Level));
357 if (Level > 1000) throw Exception("Directory level too deep: " + ToString(Level));
358 IntListPtr dirIds = GetDirectoryIDs(DirId);
359
360 for (int i = 0; i < dirIds->size(); i++) {
361 RemoveDirectoryContent(dirIds->at(i), Level + 1);
362 }
363
364 RemoveAllDirectories(DirId);
365 RemoveAllInstruments(DirId);
366 }
367
368 void InstrumentsDb::RemoveDirectory(int DirId) {
369 dmsg(2,("InstrumentsDb: RemoveDirectory(DirId=%d)\n", DirId));
370 if (GetInstrumentCount(DirId) > 0 || GetDirectoryCount(DirId) > 0) {
371 throw Exception("The specified DB directory is not empty");
372 }
373
374 std::stringstream sql;
375 sql << "DELETE FROM instr_dirs WHERE dir_id=" << DirId;
376
377 ExecSql(sql.str());
378 }
379
380 void InstrumentsDb::RemoveAllDirectories(int DirId) {
381 dmsg(2,("InstrumentsDb: RemoveAllDirectories(DirId=%d)\n", DirId));
382 IntListPtr dirIds = GetDirectoryIDs(DirId);
383
384 for (int i = 0; i < dirIds->size(); i++) {
385 if (!IsDirectoryEmpty(dirIds->at(i))) {
386 throw Exception("DB directory not empty!");
387 }
388 }
389 std::stringstream sql;
390 sql << "DELETE FROM instr_dirs WHERE parent_dir_id=" << DirId;
391 sql << " AND dir_id!=0";
392
393 ExecSql(sql.str());
394 }
395
396 bool InstrumentsDb::IsDirectoryEmpty(int DirId) {
397 dmsg(2,("InstrumentsDb: IsDirectoryEmpty(DirId=%d)\n", DirId));
398 int dirCount = GetDirectoryCount(DirId);
399 int instrCount = GetInstrumentCount(DirId);
400 dmsg(3,("InstrumentsDb: IsDirectoryEmpty: dirCount=%d,instrCount=%d\n", dirCount, instrCount));
401 if (dirCount == -1 || instrCount == -1) return false;
402 return dirCount == 0 && instrCount == 0;
403 }
404
405 bool InstrumentsDb::DirectoryExist(String Dir) {
406 dmsg(2,("InstrumentsDb: DirectoryExist(Dir=%s)\n", Dir.c_str()));
407 bool b;
408
409 DbInstrumentsMutex.Lock();
410 try { b = GetDirectoryId(Dir) != -1; }
411 catch (Exception e) {
412 DbInstrumentsMutex.Unlock();
413 throw e;
414 }
415 DbInstrumentsMutex.Unlock();
416
417 return b;
418 }
419
420 DbDirectory InstrumentsDb::GetDirectoryInfo(String Dir) {
421 dmsg(2,("InstrumentsDb: GetDirectoryInfo(Dir=%s)\n", Dir.c_str()));
422 DbDirectory d;
423
424 BeginTransaction();
425
426 try {
427 int id = GetDirectoryId(Dir);
428 if(id == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
429
430 sqlite3_stmt *pStmt = NULL;
431 std::stringstream sql;
432 sql << "SELECT created,modified,description FROM instr_dirs ";
433 sql << "WHERE dir_id=" << id;
434
435 int res = sqlite3_prepare(GetDb(), sql.str().c_str(), -1, &pStmt, NULL);
436 if (res != SQLITE_OK) {
437 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
438 }
439
440 res = sqlite3_step(pStmt);
441 if(res == SQLITE_ROW) {
442 d.Created = ToString(sqlite3_column_text(pStmt, 0));
443 d.Modified = ToString(sqlite3_column_text(pStmt, 1));
444 d.Description = ToString(sqlite3_column_text(pStmt, 2));
445 } else {
446 sqlite3_finalize(pStmt);
447
448 if (res != SQLITE_DONE) {
449 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
450 } else {
451 throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
452 }
453 }
454
455 sqlite3_finalize(pStmt);
456 } catch (Exception e) {
457 EndTransaction();
458 throw e;
459 }
460
461 EndTransaction();
462 return d;
463 }
464
465 void InstrumentsDb::RenameDirectory(String Dir, String Name) {
466 dmsg(2,("InstrumentsDb: RenameDirectory(Dir=%s,Name=%s)\n", Dir.c_str(), Name.c_str()));
467 CheckFileName(Name);
468 String dbName = toDbName(Name);
469
470 BeginTransaction();
471 try {
472 int dirId = GetDirectoryId(Dir);
473 if (dirId == -1) throw Exception("Unknown DB directory: " + toEscapedText(Dir));
474
475 std::stringstream sql;
476 sql << "SELECT parent_dir_id FROM instr_dirs WHERE dir_id=" << dirId;
477
478 int parent = ExecSqlInt(sql.str());
479 if (parent == -1) throw Exception("Unknown parent directory: " + toEscapedPath(Dir));
480
481 if (GetDirectoryId(parent, dbName) != -1) {
482 String s = toEscapedPath(Name);
483 throw Exception("Cannot rename. Directory with that name already exists: " + s);
484 }
485
486 if (GetInstrumentId(parent, dbName) != -1) {
487 throw Exception("Cannot rename. Instrument with that name exist: " + toEscapedPath(Dir));
488 }
489
490 sql.str("");
491 sql << "UPDATE instr_dirs SET dir_name=? WHERE dir_id=" << dirId;
492 ExecSql(sql.str(), dbName);
493 } catch (Exception e) {
494 EndTransaction();
495 throw e;
496 }
497
498 EndTransaction();
499 FireDirectoryNameChanged(Dir, Name);
500 }
501
502 void InstrumentsDb::MoveDirectory(String Dir, String Dst) {
503 dmsg(2,("InstrumentsDb: MoveDirectory(Dir=%s,Dst=%s)\n", Dir.c_str(), Dst.c_str()));
504
505 if(Dir.compare("/") == 0) throw Exception("Cannot move the root directory");
506 String ParentDir = GetParentDirectory(Dir);
507 if(ParentDir.empty()) throw Exception("Unknown parent directory");
508
509 BeginTransaction();
510 try {
511 int dirId = GetDirectoryId(Dir);
512 if (dirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
513 int dstId = GetDirectoryId(Dst);
514 if (dstId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dst));
515 if (dirId == dstId) {
516 throw Exception("Cannot move directory to itself");
517 }
518
519 if (Dir.at(Dir.length() - 1) != '/') Dir.append("/");
520 if (Dst.length() > Dir.length()) {
521 if (Dir.compare(Dst.substr(0, Dir.length())) == 0) {
522 throw Exception("Cannot move a directory to a subdirectory of itself.");
523 }
524 }
525
526 Dir.erase(Dir.length() - 1);
527 String dirName = GetFileName(Dir);
528
529 int id2 = GetDirectoryId(dstId, dirName);
530 if (id2 != -1) throw Exception("DB directory already exist: " + toEscapedPath(dirName));
531 id2 = GetInstrumentId(dstId, dirName);
532 if (id2 != -1) throw Exception("Instrument with that name exist: " + toEscapedPath(dirName));
533
534 std::stringstream sql;
535 sql << "UPDATE instr_dirs SET parent_dir_id=" << dstId;
536 sql << " WHERE dir_id=" << dirId;
537 ExecSql(sql.str());
538 } catch (Exception e) {
539 EndTransaction();
540 throw e;
541 }
542
543 EndTransaction();
544 FireDirectoryCountChanged(ParentDir);
545 FireDirectoryCountChanged(Dst);
546 }
547
548 void InstrumentsDb::CopyDirectory(String Dir, String Dst) {
549 dmsg(2,("InstrumentsDb: CopyDirectory(Dir=%s,Dst=%s)\n", Dir.c_str(), Dst.c_str()));
550
551 if(Dir.compare("/") == 0) throw Exception("Cannot copy the root directory");
552 String ParentDir = GetParentDirectory(Dir);
553 if(ParentDir.empty()) throw Exception("Unknown parent directory");
554
555 BeginTransaction();
556 try {
557 int dirId = GetDirectoryId(Dir);
558 if (dirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
559 int dstId = GetDirectoryId(Dst);
560 if (dstId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dst));
561 if (dirId == dstId) {
562 throw Exception("Cannot copy directory to itself");
563 }
564
565 if (Dir.at(Dir.length() - 1) != '/') Dir.append("/");
566 if (Dst.length() > Dir.length()) {
567 if (Dir.compare(Dst.substr(0, Dir.length())) == 0) {
568 throw Exception("Cannot copy a directory to a subdirectory of itself.");
569 }
570 }
571
572 Dir.erase(Dir.length() - 1);
573 String dirName = GetFileName(Dir);
574
575 int id2 = GetDirectoryId(dstId, dirName);
576 if (id2 != -1) throw Exception("DB directory already exist: " + toEscapedPath(dirName));
577 id2 = GetInstrumentId(dstId, dirName);
578 if (id2 != -1) throw Exception("Instrument with that name exist: " + toEscapedPath(dirName));
579
580 DirectoryCopier directoryCopier(ParentDir, Dst);
581 DirectoryTreeWalk(Dir, &directoryCopier);
582 } catch (Exception e) {
583 EndTransaction();
584 throw e;
585 }
586
587 EndTransaction();
588 }
589
590 void InstrumentsDb::SetDirectoryDescription(String Dir, String Desc) {
591 dmsg(2,("InstrumentsDb: SetDirectoryDescription(Dir=%s,Desc=%s)\n", Dir.c_str(), Desc.c_str()));
592
593 BeginTransaction();
594 try {
595 int id = GetDirectoryId(Dir);
596 if(id == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
597
598 std::stringstream sql;
599 sql << "UPDATE instr_dirs SET description=?,modified=CURRENT_TIMESTAMP ";
600 sql << "WHERE dir_id="<< id;
601
602 ExecSql(sql.str(), Desc);
603 } catch (Exception e) {
604 EndTransaction();
605 throw e;
606 }
607 EndTransaction();
608
609 FireDirectoryInfoChanged(Dir);
610 }
611
612 int InstrumentsDb::AddInstruments(ScanMode Mode, String DbDir, String FsDir, bool bBackground) {
613 dmsg(2,("InstrumentsDb: AddInstruments(Mode=%d,DbDir=%s,FsDir=%s,bBackground=%d)\n", Mode, DbDir.c_str(), FsDir.c_str(), bBackground));
614 if(!bBackground) {
615 switch (Mode) {
616 case NON_RECURSIVE:
617 AddInstrumentsNonrecursive(DbDir, FsDir);
618 break;
619 case RECURSIVE:
620 AddInstrumentsRecursive(DbDir, FsDir);
621 break;
622 case FLAT:
623 AddInstrumentsRecursive(DbDir, FsDir, true);
624 break;
625 default:
626 throw Exception("Unknown scan mode");
627 }
628
629 return -1;
630 }
631
632 ScanJob job;
633 int jobId = Jobs.AddJob(job);
634 InstrumentsDbThread.Execute(new AddInstrumentsJob(jobId, Mode, DbDir, FsDir));
635
636 return jobId;
637 }
638
639 int InstrumentsDb::AddInstruments(String DbDir, String FilePath, int Index, bool bBackground) {
640 dmsg(2,("InstrumentsDb: AddInstruments(DbDir=%s,FilePath=%s,Index=%d,bBackground=%d)\n", DbDir.c_str(), FilePath.c_str(), Index, bBackground));
641 if(!bBackground) {
642 AddInstruments(DbDir, FilePath, Index);
643 return -1;
644 }
645
646 ScanJob job;
647 int jobId = Jobs.AddJob(job);
648 InstrumentsDbThread.Execute(new AddInstrumentsFromFileJob(jobId, DbDir, FilePath, Index));
649
650 return jobId;
651 }
652
653 void InstrumentsDb::AddInstruments(String DbDir, String FilePath, int Index, ScanProgress* pProgress) {
654 dmsg(2,("InstrumentsDb: AddInstruments(DbDir=%s,FilePath=%s,Index=%d)\n", DbDir.c_str(), FilePath.c_str(), Index));
655 if (DbDir.empty() || FilePath.empty()) return;
656
657 DbInstrumentsMutex.Lock();
658 try {
659 int dirId = GetDirectoryId(DbDir);
660 if (dirId == -1) throw Exception("Invalid DB directory: " + toEscapedText(DbDir));
661
662 struct stat statBuf;
663 int res = stat(FilePath.c_str(), &statBuf);
664 if (res) {
665 std::stringstream ss;
666 ss << "Fail to stat `" << FilePath << "`: " << strerror(errno);
667 throw Exception(ss.str());
668 }
669
670 if (!S_ISREG(statBuf.st_mode)) {
671 std::stringstream ss;
672 ss << "`" << FilePath << "` is not an instrument file";
673 throw Exception(ss.str());
674 }
675
676 AddInstrumentsFromFile(DbDir, FilePath, Index, pProgress);
677 } catch (Exception e) {
678 DbInstrumentsMutex.Unlock();
679 throw e;
680 }
681
682 DbInstrumentsMutex.Unlock();
683 }
684
685 void InstrumentsDb::AddInstrumentsNonrecursive(String DbDir, String FsDir, ScanProgress* pProgress) {
686 dmsg(2,("InstrumentsDb: AddInstrumentsNonrecursive(DbDir=%s,FsDir=%s)\n", DbDir.c_str(), FsDir.c_str()));
687 if (DbDir.empty() || FsDir.empty()) return;
688
689 DbInstrumentsMutex.Lock();
690 try {
691 int dirId = GetDirectoryId(DbDir);
692 if (dirId == -1) throw Exception("Invalid DB directory: " + toEscapedPath(DbDir));
693
694 struct stat statBuf;
695 int res = stat(FsDir.c_str(), &statBuf);
696 if (res) {
697 std::stringstream ss;
698 ss << "Fail to stat `" << FsDir << "`: " << strerror(errno);
699 throw Exception(ss.str());
700 }
701
702 if (!S_ISDIR(statBuf.st_mode)) {
703 throw Exception("Directory expected");
704 }
705
706 if (FsDir.at(FsDir.length() - 1) != '/') FsDir.append("/");
707
708 DIR* pDir = opendir(FsDir.c_str());
709 if (pDir == NULL) {
710 std::stringstream ss;
711 ss << "The scanning of directory `" << FsDir << "` failed: ";
712 ss << strerror(errno);
713 std::cerr << ss.str();
714 DbInstrumentsMutex.Unlock();
715 return;
716 }
717
718 struct dirent* pEnt = readdir(pDir);
719 while (pEnt != NULL) {
720 if (pEnt->d_type != DT_REG) {
721 pEnt = readdir(pDir);
722 continue;
723 }
724
725 AddInstrumentsFromFile(DbDir, FsDir + String(pEnt->d_name), -1, pProgress);
726 pEnt = readdir(pDir);
727 }
728
729 if (closedir(pDir)) {
730 std::stringstream ss;
731 ss << "Failed to close directory `" << FsDir << "`: ";
732 ss << strerror(errno);
733 std::cerr << ss.str();
734 }
735 } catch (Exception e) {
736 DbInstrumentsMutex.Unlock();
737 throw e;
738 }
739
740 DbInstrumentsMutex.Unlock();
741 }
742
743 void InstrumentsDb::AddInstrumentsRecursive(String DbDir, String FsDir, bool Flat, ScanProgress* pProgress) {
744 dmsg(2,("InstrumentsDb: AddInstrumentsRecursive(DbDir=%s,FsDir=%s,Flat=%d)\n", DbDir.c_str(), FsDir.c_str(), Flat));
745 if (pProgress != NULL) {
746 pProgress->SetTotalFileCount(InstrumentFileCounter::Count(FsDir));
747 }
748
749 DirectoryScanner::Scan(DbDir, FsDir, Flat, pProgress);
750 }
751
752 int InstrumentsDb::GetInstrumentCount(int DirId) {
753 dmsg(2,("InstrumentsDb: GetInstrumentCount(DirId=%d)\n", DirId));
754 if(DirId == -1) return -1;
755
756 std::stringstream sql;
757 sql << "SELECT COUNT(*) FROM instruments WHERE dir_id=" << DirId;
758
759 return ExecSqlInt(sql.str());
760 }
761
762 int InstrumentsDb::GetInstrumentCount(String Dir, bool Recursive) {
763 dmsg(2,("InstrumentsDb: GetInstrumentCount(Dir=%s,Recursive=%d)\n", Dir.c_str(), Recursive));
764 int i;
765
766 BeginTransaction();
767 try {
768 if (Recursive) {
769 InstrumentCounter instrumentCounter;
770 DirectoryTreeWalk(Dir, &instrumentCounter);
771 i = instrumentCounter.GetInstrumentCount();
772 } else {
773 i = GetInstrumentCount(GetDirectoryId(Dir));
774 }
775 } catch (Exception e) {
776 EndTransaction();
777 throw e;
778 }
779 EndTransaction();
780
781 if (i == -1) throw Exception("Unknown Db directory: " + toEscapedPath(Dir));
782 return i;
783 }
784
785 IntListPtr InstrumentsDb::GetInstrumentIDs(int DirId) {
786 std::stringstream sql;
787 sql << "SELECT instr_id FROM instruments WHERE dir_id=" << DirId;
788
789 return ExecSqlIntList(sql.str());
790 }
791
792 StringListPtr InstrumentsDb::GetInstruments(String Dir, bool Recursive) {
793 dmsg(2,("InstrumentsDb: GetInstruments(Dir=%s,Recursive=%d)\n", Dir.c_str(), Recursive));
794 BeginTransaction();
795 try {
796 int dirId = GetDirectoryId(Dir);
797 if(dirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
798
799 StringListPtr pInstrs;
800
801 if(Recursive) {
802 SearchQuery q;
803 InstrumentFinder instrumentFinder(&q);
804 DirectoryTreeWalk(Dir, &instrumentFinder);
805 pInstrs = instrumentFinder.GetInstruments();
806 } else {
807 std::stringstream sql;
808 sql << "SELECT instr_name FROM instruments WHERE dir_id=" << dirId;
809
810 pInstrs = ExecSqlStringList(sql.str());
811 }
812 EndTransaction();
813 return pInstrs;
814 } catch (Exception e) {
815 EndTransaction();
816 throw e;
817 }
818 }
819
820 int InstrumentsDb::GetInstrumentId(String Instr) {
821 dmsg(2,("InstrumentsDb: GetInstrumentId(Instr=%s)\n", Instr.c_str()));
822 String Dir = GetDirectoryPath(Instr);
823 if (Dir.empty()) return -1;
824
825 return GetInstrumentId(GetDirectoryId(Dir), GetFileName(Instr));
826 }
827
828 int InstrumentsDb::GetInstrumentId(int DirId, String InstrName) {
829 dmsg(2,("InstrumentsDb: GetInstrumentId(DirId=%d,InstrName=%s)\n", DirId, InstrName.c_str()));
830 if (DirId == -1 || InstrName.empty()) return -1;
831
832 std::stringstream sql;
833 sql << "SELECT instr_id FROM instruments WHERE dir_id=";
834 sql << DirId << " AND instr_name=?";
835 return ExecSqlInt(sql.str(), toDbName(InstrName));
836 }
837
838 String InstrumentsDb::GetInstrumentName(int InstrId) {
839 dmsg(2,("InstrumentsDb: GetInstrumentName(InstrId=%d)\n", InstrId));
840 std::stringstream sql;
841 sql << "SELECT instr_name FROM instruments WHERE instr_id=" << InstrId;
842 return toAbstractName(ExecSqlString(sql.str()));
843 }
844
845 void InstrumentsDb::RemoveInstrument(String Instr) {
846 dmsg(2,("InstrumentsDb: RemoveInstrument(Instr=%s)\n", Instr.c_str()));
847 String ParentDir = GetDirectoryPath(Instr);
848 if(ParentDir.empty()) throw Exception("Unknown parent directory");
849
850 BeginTransaction();
851 try {
852 int instrId = GetInstrumentId(Instr);
853 if(instrId == -1) {
854 throw Exception("The specified instrument does not exist: " + toEscapedPath(Instr));
855 }
856 RemoveInstrument(instrId);
857 } catch (Exception e) {
858 EndTransaction();
859 throw e;
860 }
861 EndTransaction();
862 FireInstrumentCountChanged(ParentDir);
863 }
864
865 void InstrumentsDb::RemoveInstrument(int InstrId) {
866 dmsg(2,("InstrumentsDb: RemoveInstrument(InstrId=%d)\n", InstrId));
867
868 std::stringstream sql;
869 sql << "DELETE FROM instruments WHERE instr_id=" << InstrId;
870
871 ExecSql(sql.str());
872 }
873
874 void InstrumentsDb::RemoveAllInstruments(int DirId) {
875 dmsg(2,("InstrumentsDb: RemoveAllInstruments(DirId=%d)\n", DirId));
876
877 std::stringstream sql;
878 sql << "DELETE FROM instruments WHERE dir_id=" << DirId;
879 ExecSql(sql.str());
880 }
881
882 DbInstrument InstrumentsDb::GetInstrumentInfo(String Instr) {
883 dmsg(2,("InstrumentsDb: GetInstrumentInfo(Instr=%s)\n", Instr.c_str()));
884 DbInstrument i;
885
886 BeginTransaction();
887 try {
888 int id = GetInstrumentId(Instr);
889 if(id == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
890 i = GetInstrumentInfo(id);
891 } catch (Exception e) {
892 EndTransaction();
893 throw e;
894 }
895 EndTransaction();
896
897 return i;
898 }
899
900 DbInstrument InstrumentsDb::GetInstrumentInfo(int InstrId) {
901 sqlite3_stmt *pStmt = NULL;
902 std::stringstream sql;
903 sql << "SELECT instr_file,instr_nr,format_family,format_version,";
904 sql << "instr_size,created,modified,description,is_drum,product,";
905 sql << "artists,keywords FROM instruments WHERE instr_id=" << InstrId;
906
907 int res = sqlite3_prepare(GetDb(), sql.str().c_str(), -1, &pStmt, NULL);
908 if (res != SQLITE_OK) {
909 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
910 }
911
912 DbInstrument i;
913 res = sqlite3_step(pStmt);
914 if(res == SQLITE_ROW) {
915 i.InstrFile = ToString(sqlite3_column_text(pStmt, 0));
916 i.InstrNr = sqlite3_column_int(pStmt, 1);
917 i.FormatFamily = ToString(sqlite3_column_text(pStmt, 2));
918 i.FormatVersion = ToString(sqlite3_column_text(pStmt, 3));
919 i.Size = sqlite3_column_int64(pStmt, 4);
920 i.Created = ToString(sqlite3_column_text(pStmt, 5));
921 i.Modified = ToString(sqlite3_column_text(pStmt, 6));
922 i.Description = ToString(sqlite3_column_text(pStmt, 7));
923 i.IsDrum = sqlite3_column_int(pStmt, 8);
924 i.Product = ToString(sqlite3_column_text(pStmt, 9));
925 i.Artists = ToString(sqlite3_column_text(pStmt, 10));
926 i.Keywords = ToString(sqlite3_column_text(pStmt, 11));
927 } else {
928 sqlite3_finalize(pStmt);
929
930 if (res != SQLITE_DONE) {
931 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
932 } else {
933 throw Exception("Unknown DB instrument");
934 }
935 }
936
937 sqlite3_finalize(pStmt);
938 return i;
939 }
940
941 void InstrumentsDb::RenameInstrument(String Instr, String Name) {
942 dmsg(2,("InstrumentsDb: RenameInstrument(Instr=%s,Name=%s)\n", Instr.c_str(), Name.c_str()));
943 CheckFileName(Name);
944
945 BeginTransaction();
946 try {
947 int dirId = GetDirectoryId(GetDirectoryPath(Instr));
948 if (dirId == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
949
950 int instrId = GetInstrumentId(dirId, GetFileName(Instr));
951 if (instrId == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
952
953 if (GetInstrumentId(dirId, Name) != -1) {
954 String s = toEscapedPath(Name);
955 throw Exception("Cannot rename. Instrument with that name already exists: " + s);
956 }
957
958 if (GetDirectoryId(dirId, Name) != -1) {
959 String s = toEscapedPath(Name);
960 throw Exception("Cannot rename. Directory with that name already exists: " + s);
961 }
962
963 std::stringstream sql;
964 sql << "UPDATE instruments SET instr_name=? WHERE instr_id=" << instrId;
965 ExecSql(sql.str(), toDbName(Name));
966 } catch (Exception e) {
967 EndTransaction();
968 throw e;
969 }
970 EndTransaction();
971 FireInstrumentNameChanged(Instr, Name);
972 }
973
974 void InstrumentsDb::MoveInstrument(String Instr, String Dst) {
975 dmsg(2,("InstrumentsDb: MoveInstrument(Instr=%s,Dst=%s)\n", Instr.c_str(), Dst.c_str()));
976 String ParentDir = GetDirectoryPath(Instr);
977 if(ParentDir.empty()) throw Exception("Unknown parent directory");
978
979 BeginTransaction();
980 try {
981 int dirId = GetDirectoryId(ParentDir);
982 if (dirId == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
983
984 String instrName = GetFileName(Instr);
985 int instrId = GetInstrumentId(dirId, instrName);
986 if (instrId == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
987
988 int dstId = GetDirectoryId(Dst);
989 if (dstId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dst));
990 if (dirId == dstId) {
991 EndTransaction();
992 return;
993 }
994
995 if (GetInstrumentId(dstId, instrName) != -1) {
996 String s = toEscapedPath(instrName);
997 throw Exception("Cannot move. Instrument with that name already exists: " + s);
998 }
999
1000 if (GetDirectoryId(dstId, instrName) != -1) {
1001 String s = toEscapedPath(instrName);
1002 throw Exception("Cannot move. Directory with that name already exists: " + s);
1003 }
1004
1005 std::stringstream sql;
1006 sql << "UPDATE instruments SET dir_id=" << dstId;
1007 sql << " WHERE instr_id=" << instrId;
1008 ExecSql(sql.str());
1009 } catch (Exception e) {
1010 EndTransaction();
1011 throw e;
1012 }
1013 EndTransaction();
1014 FireInstrumentCountChanged(ParentDir);
1015 FireInstrumentCountChanged(Dst);
1016 }
1017
1018 void InstrumentsDb::CopyInstrument(String Instr, String Dst) {
1019 dmsg(2,("InstrumentsDb: CopyInstrument(Instr=%s,Dst=%s)\n", Instr.c_str(), Dst.c_str()));
1020 String ParentDir = GetDirectoryPath(Instr);
1021 if(ParentDir.empty()) throw Exception("Unknown parent directory");
1022
1023 BeginTransaction();
1024 try {
1025 int dirId = GetDirectoryId(GetDirectoryPath(Instr));
1026 if (dirId == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
1027
1028 String instrName = GetFileName(Instr);
1029 int instrId = GetInstrumentId(dirId, instrName);
1030 if (instrId == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
1031
1032 int dstId = GetDirectoryId(Dst);
1033 if (dstId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dst));
1034 if (dirId == dstId) {
1035 EndTransaction();
1036 return;
1037 }
1038
1039 if (GetInstrumentId(dstId, instrName) != -1) {
1040 String s = toEscapedPath(instrName);
1041 throw Exception("Cannot copy. Instrument with that name already exists: " + s);
1042 }
1043
1044 if (GetDirectoryId(dstId, instrName) != -1) {
1045 String s = toEscapedPath(instrName);
1046 throw Exception("Cannot copy. Directory with that name already exists: " + s);
1047 }
1048
1049 CopyInstrument(instrId, instrName, dstId, Dst);
1050 } catch (Exception e) {
1051 EndTransaction();
1052 throw e;
1053 }
1054 EndTransaction();
1055
1056 }
1057
1058 void InstrumentsDb::CopyInstrument(int InstrId, String InstrName, int DstDirId, String DstDir) {
1059 DbInstrument i = GetInstrumentInfo(InstrId);
1060 sqlite3_stmt *pStmt = NULL;
1061 std::stringstream sql;
1062 sql << "INSERT INTO instruments (dir_id,instr_name,instr_file,instr_nr,format_family,";
1063 sql << "format_version,instr_size,description,is_drum,product,artists,keywords) ";
1064 sql << "VALUES (" << DstDirId << ",?,?," << i.InstrNr << ",?,?," << i.Size << ",?,";
1065 sql << i.IsDrum << ",?,?,?)";
1066
1067 int res = sqlite3_prepare(GetDb(), sql.str().c_str(), -1, &pStmt, NULL);
1068 if (res != SQLITE_OK) {
1069 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1070 }
1071
1072 BindTextParam(pStmt, 1, toDbName(InstrName));
1073 BindTextParam(pStmt, 2, i.InstrFile);
1074 BindTextParam(pStmt, 3, i.FormatFamily);
1075 BindTextParam(pStmt, 4, i.FormatVersion);
1076 BindTextParam(pStmt, 5, i.Description);
1077 BindTextParam(pStmt, 6, i.Product);
1078 BindTextParam(pStmt, 7, i.Artists);
1079 BindTextParam(pStmt, 8, i.Keywords);
1080
1081 res = sqlite3_step(pStmt);
1082 if(res != SQLITE_DONE) {
1083 sqlite3_finalize(pStmt);
1084 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1085 }
1086
1087 sqlite3_finalize(pStmt);
1088 FireInstrumentCountChanged(DstDir);
1089 }
1090
1091 void InstrumentsDb::SetInstrumentDescription(String Instr, String Desc) {
1092 dmsg(2,("InstrumentsDb: SetInstrumentDescription(Instr=%s,Desc=%s)\n", Instr.c_str(), Desc.c_str()));
1093
1094 BeginTransaction();
1095 try {
1096 int id = GetInstrumentId(Instr);
1097 if(id == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
1098
1099 std::stringstream sql;
1100 sql << "UPDATE instruments SET description=?,modified=CURRENT_TIMESTAMP ";
1101 sql << "WHERE instr_id="<< id;
1102
1103 ExecSql(sql.str(), Desc);
1104 } catch (Exception e) {
1105 EndTransaction();
1106 throw e;
1107 }
1108 EndTransaction();
1109 FireInstrumentInfoChanged(Instr);
1110 }
1111
1112 void InstrumentsDb::AddInstrumentsFromFile(String DbDir, String File, int Index, ScanProgress* pProgress) {
1113 dmsg(2,("InstrumentsDb: AddInstrumentsFromFile(DbDir=%s,File=%s,Index=%d)\n", DbDir.c_str(), File.c_str(), Index));
1114
1115 if(File.length() < 4) return;
1116
1117 try {
1118 if(!strcasecmp(".gig", File.substr(File.length() - 4).c_str())) {
1119 if (pProgress != NULL) {
1120 pProgress->SetStatus(0);
1121 pProgress->CurrentFile = File;
1122 }
1123
1124 AddGigInstruments(DbDir, File, Index, pProgress);
1125
1126 if (pProgress != NULL) {
1127 pProgress->SetScannedFileCount(pProgress->GetScannedFileCount() + 1);
1128 }
1129 }
1130 } catch(Exception e) {
1131 std::cerr << e.Message() << std::endl;
1132 }
1133 }
1134
1135 void InstrumentsDb::AddGigInstruments(String DbDir, String File, int Index, ScanProgress* pProgress) {
1136 dmsg(2,("InstrumentsDb: AddGigInstruments(DbDir=%s,File=%s,Index=%d)\n", DbDir.c_str(), File.c_str(), Index));
1137 int dirId = GetDirectoryId(DbDir);
1138 if (dirId == -1) throw Exception("Invalid DB directory: " + toEscapedPath(DbDir));
1139
1140 struct stat statBuf;
1141 int res = stat(File.c_str(), &statBuf);
1142 if (res) {
1143 std::stringstream ss;
1144 ss << "Fail to stat `" << File << "`: " << strerror(errno);
1145 throw Exception(ss.str());
1146 }
1147
1148 if (!S_ISREG(statBuf.st_mode)) {
1149 std::stringstream ss;
1150 ss << "`" << File << "` is not a regular file";
1151 throw Exception(ss.str());
1152 }
1153
1154 RIFF::File* riff = NULL;
1155 gig::File* gig = NULL;
1156 try {
1157 riff = new RIFF::File(File);
1158 gig::File* gig = new gig::File(riff);
1159
1160 std::stringstream sql;
1161 sql << "INSERT INTO instruments (dir_id,instr_name,instr_file,";
1162 sql << "instr_nr,format_family,format_version,instr_size,";
1163 sql << "description,is_drum,product,artists,keywords) VALUES (";
1164 sql << dirId << ",?,?,?,'GIG',?," << statBuf.st_size << ",?,?,?,?,?)";
1165
1166 sqlite3_stmt* pStmt = NULL;
1167
1168 int res = sqlite3_prepare(GetDb(), sql.str().c_str(), -1, &pStmt, NULL);
1169 if (res != SQLITE_OK) {
1170 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1171 }
1172
1173 BindTextParam(pStmt, 2, File);
1174 String ver = "";
1175 if (gig->pVersion != NULL) ver = ToString(gig->pVersion->major);
1176 BindTextParam(pStmt, 4, ver);
1177
1178 if (Index == -1) {
1179 int instrIndex = 0;
1180 if (pProgress != NULL) gig->GetInstrument(0, &(pProgress->GigFileProgress)); // TODO: this workaround should be fixed
1181 gig::Instrument* pInstrument = gig->GetFirstInstrument();
1182 while (pInstrument) {
1183 BindTextParam(pStmt, 7, gig->pInfo->Product);
1184 BindTextParam(pStmt, 8, gig->pInfo->Artists);
1185 BindTextParam(pStmt, 9, gig->pInfo->Keywords);
1186 AddGigInstrument(pStmt, DbDir, dirId, File, pInstrument, instrIndex);
1187
1188 instrIndex++;
1189 pInstrument = gig->GetNextInstrument();
1190 }
1191 } else {
1192 gig::Instrument* pInstrument;
1193 if (pProgress == NULL) pInstrument = gig->GetInstrument(Index);
1194 else pInstrument = gig->GetInstrument(Index, &(pProgress->GigFileProgress));
1195 if (pInstrument != NULL) {
1196 BindTextParam(pStmt, 7, gig->pInfo->Product);
1197 BindTextParam(pStmt, 8, gig->pInfo->Artists);
1198 BindTextParam(pStmt, 9, gig->pInfo->Keywords);
1199 AddGigInstrument(pStmt, DbDir, dirId, File, pInstrument, Index);
1200 }
1201 }
1202
1203 sqlite3_finalize(pStmt);
1204 delete gig;
1205 delete riff;
1206 } catch (RIFF::Exception e) {
1207 if (gig != NULL) delete gig;
1208 if (riff != NULL) delete riff;
1209 std::stringstream ss;
1210 ss << "Failed to scan `" << File << "`: " << e.Message;
1211
1212 throw Exception(ss.str());
1213 } catch (Exception e) {
1214 if (gig != NULL) delete gig;
1215 if (riff != NULL) delete riff;
1216 throw e;
1217 } catch (...) {
1218 if (gig != NULL) delete gig;
1219 if (riff != NULL) delete riff;
1220 throw Exception("Failed to scan `" + File + "`");
1221 }
1222 }
1223
1224 void InstrumentsDb::AddGigInstrument(sqlite3_stmt* pStmt, String DbDir, int DirId, String File, gig::Instrument* pInstrument, int Index) {
1225 String name = pInstrument->pInfo->Name;
1226 if (name == "") return;
1227 name = GetUniqueInstrumentName(DirId, name);
1228
1229 std::stringstream sql2;
1230 sql2 << "SELECT COUNT(*) FROM instruments WHERE instr_file=? AND ";
1231 sql2 << "instr_nr=" << Index;
1232 if (ExecSqlInt(sql2.str(), File) > 0) return;
1233
1234 BindTextParam(pStmt, 1, name);
1235 BindIntParam(pStmt, 3, Index);
1236
1237 BindTextParam(pStmt, 5, pInstrument->pInfo->Comments);
1238 BindIntParam(pStmt, 6, pInstrument->IsDrum);
1239
1240 if (!pInstrument->pInfo->Product.empty()) {
1241 BindTextParam(pStmt, 7, pInstrument->pInfo->Product);
1242 }
1243 if (!pInstrument->pInfo->Artists.empty()) {
1244 BindTextParam(pStmt, 8, pInstrument->pInfo->Artists);
1245 }
1246
1247 if (!pInstrument->pInfo->Keywords.empty()) {
1248 BindTextParam(pStmt, 9, pInstrument->pInfo->Keywords);
1249 }
1250
1251 int res = sqlite3_step(pStmt);
1252 if(res != SQLITE_DONE) {
1253 sqlite3_finalize(pStmt);
1254 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1255 }
1256
1257 res = sqlite3_reset(pStmt);
1258 FireInstrumentCountChanged(DbDir);
1259 }
1260
1261 void InstrumentsDb::DirectoryTreeWalk(String AbstractPath, DirectoryHandler* pHandler) {
1262 int DirId = GetDirectoryId(AbstractPath);
1263 if(DirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(AbstractPath));
1264 DirectoryTreeWalk(pHandler, AbstractPath, DirId, 0);
1265 }
1266
1267 void InstrumentsDb::DirectoryTreeWalk(DirectoryHandler* pHandler, String AbstractPath, int DirId, int Level) {
1268 if(Level == 1000) throw Exception("Possible infinite loop detected");
1269 pHandler->ProcessDirectory(AbstractPath, DirId);
1270
1271 String s;
1272 StringListPtr pDirs = GetDirectories(DirId);
1273 for(int i = 0; i < pDirs->size(); i++) {
1274 if (AbstractPath.length() == 1 && AbstractPath.at(0) == '/') {
1275 s = "/" + pDirs->at(i);
1276 } else {
1277 s = AbstractPath + "/" + pDirs->at(i);
1278 }
1279 DirectoryTreeWalk(pHandler, s, GetDirectoryId(DirId, pDirs->at(i)), Level + 1);
1280 }
1281 }
1282
1283 StringListPtr InstrumentsDb::FindDirectories(String Dir, SearchQuery* pQuery, bool Recursive) {
1284 dmsg(2,("InstrumentsDb: FindDirectories(Dir=%s)\n", Dir.c_str()));
1285 DirectoryFinder directoryFinder(pQuery);
1286
1287 BeginTransaction();
1288 try {
1289 int DirId = GetDirectoryId(Dir);
1290 if(DirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
1291
1292 if (Recursive) DirectoryTreeWalk(Dir, &directoryFinder);
1293 else directoryFinder.ProcessDirectory(Dir, DirId);
1294 } catch (Exception e) {
1295 EndTransaction();
1296 throw e;
1297 }
1298 EndTransaction();
1299
1300 return directoryFinder.GetDirectories();
1301 }
1302
1303 StringListPtr InstrumentsDb::FindInstruments(String Dir, SearchQuery* pQuery, bool Recursive) {
1304 dmsg(2,("InstrumentsDb: FindInstruments(Dir=%s)\n", Dir.c_str()));
1305 InstrumentFinder instrumentFinder(pQuery);
1306
1307 BeginTransaction();
1308 try {
1309 int DirId = GetDirectoryId(Dir);
1310 if(DirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
1311
1312 if (Recursive) DirectoryTreeWalk(Dir, &instrumentFinder);
1313 else instrumentFinder.ProcessDirectory(Dir, DirId);
1314 } catch (Exception e) {
1315 EndTransaction();
1316 throw e;
1317 }
1318 EndTransaction();
1319
1320 return instrumentFinder.GetInstruments();
1321 }
1322
1323 void InstrumentsDb::BeginTransaction() {
1324 dmsg(2,("InstrumentsDb: BeginTransaction(InTransaction=%d)\n", InTransaction));
1325 DbInstrumentsMutex.Lock();
1326 if (InTransaction) return;
1327
1328 if(db == NULL) return;
1329 sqlite3_stmt *pStmt = NULL;
1330
1331 InTransaction = true;
1332 int res = sqlite3_prepare(db, "BEGIN TRANSACTION", -1, &pStmt, NULL);
1333 if (res != SQLITE_OK) {
1334 std::cerr << ToString(sqlite3_errmsg(db)) << std::endl;
1335 return;
1336 }
1337
1338 res = sqlite3_step(pStmt);
1339 if(res != SQLITE_DONE) {
1340 sqlite3_finalize(pStmt);
1341 std::cerr << ToString(sqlite3_errmsg(db)) << std::endl;
1342 return;
1343 }
1344
1345 sqlite3_finalize(pStmt);
1346 }
1347
1348 void InstrumentsDb::EndTransaction() {
1349 dmsg(2,("InstrumentsDb: EndTransaction(InTransaction=%d)\n", InTransaction));
1350 if (!InTransaction) {
1351 DbInstrumentsMutex.Unlock();
1352 return;
1353 }
1354 InTransaction = false;
1355
1356 if(db == NULL) {
1357 DbInstrumentsMutex.Unlock();
1358 return;
1359 }
1360 sqlite3_stmt *pStmt = NULL;
1361
1362 int res = sqlite3_prepare(db, "END TRANSACTION", -1, &pStmt, NULL);
1363 if (res != SQLITE_OK) {
1364 std::cerr << ToString(sqlite3_errmsg(db)) << std::endl;
1365 DbInstrumentsMutex.Unlock();
1366 return;
1367 }
1368
1369 res = sqlite3_step(pStmt);
1370 if(res != SQLITE_DONE) {
1371 sqlite3_finalize(pStmt);
1372 std::cerr << ToString(sqlite3_errmsg(db)) << std::endl;
1373 DbInstrumentsMutex.Unlock();
1374 return;
1375 }
1376
1377 sqlite3_finalize(pStmt);
1378 DbInstrumentsMutex.Unlock();
1379 }
1380
1381 void InstrumentsDb::ExecSql(String Sql) {
1382 dmsg(2,("InstrumentsDb: ExecSql(Sql=%s)\n", Sql.c_str()));
1383 sqlite3_stmt *pStmt = NULL;
1384
1385 int res = sqlite3_prepare(GetDb(), Sql.c_str(), -1, &pStmt, NULL);
1386 if (res != SQLITE_OK) {
1387 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1388 }
1389
1390 res = sqlite3_step(pStmt);
1391 if(res != SQLITE_DONE) {
1392 sqlite3_finalize(pStmt);
1393 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1394 }
1395
1396 sqlite3_finalize(pStmt);
1397 }
1398
1399 void InstrumentsDb::ExecSql(String Sql, String Param) {
1400 dmsg(2,("InstrumentsDb: ExecSql(Sql=%s,Param=%s)\n", Sql.c_str(), Param.c_str()));
1401 sqlite3_stmt *pStmt = NULL;
1402
1403 int res = sqlite3_prepare(GetDb(), Sql.c_str(), -1, &pStmt, NULL);
1404 if (res != SQLITE_OK) {
1405 sqlite3_finalize(pStmt);
1406 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1407 }
1408
1409 BindTextParam(pStmt, 1, Param);
1410
1411 res = sqlite3_step(pStmt);
1412 if (res != SQLITE_DONE) {
1413 sqlite3_finalize(pStmt);
1414 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1415 }
1416
1417 sqlite3_finalize(pStmt);
1418 }
1419
1420 int InstrumentsDb::ExecSqlInt(String Sql) {
1421 dmsg(2,("InstrumentsDb: ExecSqlInt(Sql=%s)\n", Sql.c_str()));
1422 sqlite3_stmt *pStmt = NULL;
1423
1424 int res = sqlite3_prepare(GetDb(), Sql.c_str(), -1, &pStmt, NULL);
1425 if (res != SQLITE_OK) {
1426 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1427 }
1428
1429 int i = -1;
1430 res = sqlite3_step(pStmt);
1431 if(res == SQLITE_ROW) {
1432 i = sqlite3_column_int(pStmt, 0);
1433 } else if (res != SQLITE_DONE) {
1434 sqlite3_finalize(pStmt);
1435 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1436 }
1437
1438 sqlite3_finalize(pStmt);
1439
1440 return i;
1441 }
1442
1443 int InstrumentsDb::ExecSqlInt(String Sql, String Param) {
1444 dmsg(2,("InstrumentsDb: ExecSqlInt(Sql=%s,Param=%s)\n", Sql.c_str(), Param.c_str()));
1445 sqlite3_stmt *pStmt = NULL;
1446
1447 int res = sqlite3_prepare(GetDb(), Sql.c_str(), -1, &pStmt, NULL);
1448 if (res != SQLITE_OK) {
1449 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1450 }
1451
1452 BindTextParam(pStmt, 1, Param);
1453
1454 int i = -1;
1455 res = sqlite3_step(pStmt);
1456 if(res == SQLITE_ROW) {
1457 i = sqlite3_column_int(pStmt, 0);
1458 } else if (res != SQLITE_DONE) {
1459 sqlite3_finalize(pStmt);
1460 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1461 }
1462
1463 sqlite3_finalize(pStmt);
1464 return i;
1465 }
1466
1467 String InstrumentsDb::ExecSqlString(String Sql) {
1468 dmsg(2,("InstrumentsDb: ExecSqlString(Sql=%s)\n", Sql.c_str()));
1469 sqlite3_stmt *pStmt = NULL;
1470
1471 int res = sqlite3_prepare(GetDb(), Sql.c_str(), -1, &pStmt, NULL);
1472 if (res != SQLITE_OK) {
1473 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1474 }
1475
1476 String s;
1477 res = sqlite3_step(pStmt);
1478 if(res == SQLITE_ROW) {
1479 s = ToString(sqlite3_column_text(pStmt, 0));
1480 } else if (res != SQLITE_DONE) {
1481 sqlite3_finalize(pStmt);
1482 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1483 }
1484
1485 sqlite3_finalize(pStmt);
1486
1487 return s;
1488 }
1489
1490 IntListPtr InstrumentsDb::ExecSqlIntList(String Sql) {
1491 IntListPtr intList(new std::vector<int>);
1492
1493 sqlite3_stmt *pStmt = NULL;
1494
1495 int res = sqlite3_prepare(GetDb(), Sql.c_str(), -1, &pStmt, NULL);
1496 if (res != SQLITE_OK) {
1497 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1498 }
1499
1500 res = sqlite3_step(pStmt);
1501 while(res == SQLITE_ROW) {
1502 intList->push_back(sqlite3_column_int(pStmt, 0));
1503 res = sqlite3_step(pStmt);
1504 }
1505
1506 if (res != SQLITE_DONE) {
1507 sqlite3_finalize(pStmt);
1508 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1509 }
1510
1511 sqlite3_finalize(pStmt);
1512
1513 return intList;
1514 }
1515
1516 StringListPtr InstrumentsDb::ExecSqlStringList(String Sql) {
1517 StringListPtr stringList(new std::vector<String>);
1518
1519 sqlite3_stmt *pStmt = NULL;
1520
1521 int res = sqlite3_prepare(GetDb(), Sql.c_str(), -1, &pStmt, NULL);
1522 if (res != SQLITE_OK) {
1523 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1524 }
1525
1526 res = sqlite3_step(pStmt);
1527 while(res == SQLITE_ROW) {
1528 stringList->push_back(ToString(sqlite3_column_text(pStmt, 0)));
1529 res = sqlite3_step(pStmt);
1530 }
1531
1532 if (res != SQLITE_DONE) {
1533 sqlite3_finalize(pStmt);
1534 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1535 }
1536
1537 sqlite3_finalize(pStmt);
1538
1539 return stringList;
1540 }
1541
1542 void InstrumentsDb::BindTextParam(sqlite3_stmt* pStmt, int Index, String Text) {
1543 if (pStmt == NULL) return;
1544 int res = sqlite3_bind_text(pStmt, Index, Text.c_str(), -1, SQLITE_STATIC);
1545 if (res != SQLITE_OK) {
1546 sqlite3_finalize(pStmt);
1547 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1548 }
1549 }
1550
1551 void InstrumentsDb::BindIntParam(sqlite3_stmt* pStmt, int Index, int Param) {
1552 if (pStmt == NULL) return;
1553 int res = sqlite3_bind_int(pStmt, Index, Param);
1554 if (res != SQLITE_OK) {
1555 sqlite3_finalize(pStmt);
1556 throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1557 }
1558 }
1559
1560 void InstrumentsDb::Regexp(sqlite3_context* pContext, int argc, sqlite3_value** ppValue) {
1561 if (argc != 2) return;
1562
1563 String pattern = ToString(sqlite3_value_text(ppValue[0]));
1564 String str = ToString(sqlite3_value_text(ppValue[1]));
1565
1566 if(!fnmatch(pattern.c_str(), str.c_str(), FNM_CASEFOLD)) {
1567 sqlite3_result_int(pContext, 1);
1568 }
1569 }
1570
1571 String InstrumentsDb::GetDirectoryPath(String File) {
1572 if (File.empty()) return String("");
1573 if (File.at(0) != '/') String("");
1574 if (File.length() == 1) return File;
1575 if (File.at(File.length() - 1) == '/') return File.substr(0, File.length() - 1);
1576 int i = File.rfind('/', File.length() - 1);
1577 if(i == std::string::npos) return String("");
1578 if(i == 0) return String("/");
1579 return File.substr(0, i);
1580 }
1581
1582 String InstrumentsDb::GetFileName(String Path) {
1583 if (Path.length() < 2) return String("");
1584 if (Path.at(0) != '/') String("");
1585 if (Path.at(Path.length() - 1) == '/') return String("");
1586 int i = Path.rfind('/', Path.length() - 1);
1587 return Path.substr(i + 1);
1588 }
1589
1590 void InstrumentsDb::CheckPathName(String Path) {
1591 if (Path.empty()) return;
1592
1593 int i = 0, j = Path.find('/', i);
1594
1595 while(j != std::string::npos) {
1596 if (j + 1 >= Path.length()) return;
1597 if (Path.at(j + 1) == '/') throw Exception("Invalid path name: " + Path);
1598
1599 i = j + 1;
1600 j = Path.find('/', i);
1601 }
1602 }
1603
1604 String InstrumentsDb::GetParentDirectory(String Dir) {
1605 if (Dir.length() < 2) return String("");
1606 if (Dir.at(0) != '/') String("");
1607 int i = Dir.rfind('/', Dir.length() - 2);
1608 if (i == 0) return "/";
1609 return Dir.substr(0, i);
1610 }
1611
1612 void InstrumentsDb::CheckFileName(String File) {
1613 if (File.empty()) throw Exception("Invalid file name: " + File);
1614 }
1615
1616 String InstrumentsDb::GetUniqueInstrumentName(int DirId, String Name) {
1617 dmsg(2,("InstrumentsDb: GetUniqueInstrumentName(DirId=%d,Name=%s)\n", DirId, Name.c_str()));
1618
1619 if (GetInstrumentId(DirId, Name) == -1 && GetDirectoryId(DirId, Name) == -1) return Name;
1620 std::stringstream ss;
1621 for(int i = 2; i < 1001; i++) {
1622 ss.str("");
1623 ss << Name << '[' << i << ']';
1624 if (GetInstrumentId(DirId, ss.str()) == -1 && GetInstrumentId(DirId, ss.str()) == -1) {
1625 return ss.str();
1626 }
1627 }
1628
1629 throw Exception("Unable to find an unique name: " + Name);
1630 }
1631
1632 String InstrumentsDb::toDbName(String AbstractName) {
1633 for (int i = 0; i < AbstractName.length(); i++) {
1634 if (AbstractName.at(i) == '\0') AbstractName.at(i) = '/';
1635 }
1636 return AbstractName;
1637 }
1638
1639 String InstrumentsDb::toEscapedPath(String AbstractName) {
1640 for (int i = 0; i < AbstractName.length(); i++) {
1641 if (AbstractName.at(i) == '\0') AbstractName.replace(i++, 1, "\\/");
1642 else if (AbstractName.at(i) == '\\') AbstractName.replace(i++, 1, "\\\\");
1643 else if (AbstractName.at(i) == '\'') AbstractName.replace(i++, 1, "\\'");
1644 else if (AbstractName.at(i) == '"') AbstractName.replace(i++, 1, "\\\"");
1645 else if (AbstractName.at(i) == '\r') AbstractName.replace(i++, 1, "\\r");
1646 else if (AbstractName.at(i) == '\n') AbstractName.replace(i++, 1, "\\n");
1647 }
1648 return AbstractName;
1649 }
1650
1651 String InstrumentsDb::toEscapedText(String text) {
1652 for (int i = 0; i < text.length(); i++) {
1653 if (text.at(i) == '\\') text.replace(i++, 1, "\\\\");
1654 else if (text.at(i) == '\'') text.replace(i++, 1, "\\'");
1655 else if (text.at(i) == '"') text.replace(i++, 1, "\\\"");
1656 else if (text.at(i) == '\r') text.replace(i++, 1, "\\r");
1657 else if (text.at(i) == '\n') text.replace(i++, 1, "\\n");
1658 }
1659 return text;
1660 }
1661
1662 String InstrumentsDb::toEscapedName(String AbstractName) {
1663 for (int i = 0; i < AbstractName.length(); i++) {
1664 if (AbstractName.at(i) == '\0') AbstractName.at(i) = '/';
1665 else if (AbstractName.at(i) == '\\') AbstractName.replace(i++, 1, "\\\\");
1666 else if (AbstractName.at(i) == '\'') AbstractName.replace(i++, 1, "\\'");
1667 else if (AbstractName.at(i) == '"') AbstractName.replace(i++, 1, "\\\"");
1668 else if (AbstractName.at(i) == '\r') AbstractName.replace(i++, 1, "\\r");
1669 else if (AbstractName.at(i) == '\n') AbstractName.replace(i++, 1, "\\n");
1670 }
1671 return AbstractName;
1672 }
1673
1674 String InstrumentsDb::toAbstractName(String DbName) {
1675 for (int i = 0; i < DbName.length(); i++) {
1676 if (DbName.at(i) == '/') DbName.at(i) = '\0';
1677 }
1678 return DbName;
1679 }
1680
1681 void InstrumentsDb::FireDirectoryCountChanged(String Dir) {
1682 for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {
1683 llInstrumentsDbListeners.GetListener(i)->DirectoryCountChanged(Dir);
1684 }
1685 }
1686
1687 void InstrumentsDb::FireDirectoryInfoChanged(String Dir) {
1688 for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {
1689 llInstrumentsDbListeners.GetListener(i)->DirectoryInfoChanged(Dir);
1690 }
1691 }
1692
1693 void InstrumentsDb::FireDirectoryNameChanged(String Dir, String NewName) {
1694 for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {
1695 llInstrumentsDbListeners.GetListener(i)->DirectoryNameChanged(Dir, NewName);
1696 }
1697 }
1698
1699 void InstrumentsDb::FireInstrumentCountChanged(String Dir) {
1700 for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {
1701 llInstrumentsDbListeners.GetListener(i)->InstrumentCountChanged(Dir);
1702 }
1703 }
1704
1705 void InstrumentsDb::FireInstrumentInfoChanged(String Instr) {
1706 for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {
1707 llInstrumentsDbListeners.GetListener(i)->InstrumentInfoChanged(Instr);
1708 }
1709 }
1710
1711 void InstrumentsDb::FireInstrumentNameChanged(String Instr, String NewName) {
1712 for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {
1713 llInstrumentsDbListeners.GetListener(i)->InstrumentNameChanged(Instr, NewName);
1714 }
1715 }
1716
1717 void InstrumentsDb::FireJobStatusChanged(int JobId) {
1718 for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {
1719 llInstrumentsDbListeners.GetListener(i)->JobStatusChanged(JobId);
1720 }
1721 }
1722
1723 } // namespace LinuxSampler
1724
1725 #endif // HAVE_SQLITE3

  ViewVC Help
Powered by ViewVC