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

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

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1161 by iliev, Mon Apr 16 15:51:18 2007 UTC revision 1717 by iliev, Sun Mar 16 17:43:20 2008 UTC
# Line 1  Line 1 
1  /***************************************************************************  /***************************************************************************
2   *                                                                         *   *                                                                         *
3   *   Copyright (C) 2007 Grigor Iliev                                       *   *   Copyright (C) 2007, 2008 Grigor Iliev                                 *
4   *                                                                         *   *                                                                         *
5   *   This program is free software; you can redistribute it and/or modify  *   *   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  *   *   it under the terms of the GNU General Public License as published by  *
# Line 20  Line 20 
20    
21  #include "InstrumentsDb.h"  #include "InstrumentsDb.h"
22    
23  #if HAVE_SQLITE3  #include "../common/File.h"
24    #include "../common/global_private.h"
25    
26  #include <iostream>  #include <iostream>
27  #include <sstream>  #include <sstream>
28  #include <dirent.h>  #include <vector>
29  #include <errno.h>  #include <errno.h>
30  #include <ftw.h>  #include <fnmatch.h>
31    
32  #include "../common/Exception.h"  #include "../common/Exception.h"
33    
34  namespace LinuxSampler {  namespace LinuxSampler {
35    
36      void DbInstrument::Copy(const DbInstrument& Instr) {      InstrumentsDb InstrumentsDb::instance;
         if (this == &Instr) return;  
   
         InstrFile = Instr.InstrFile;  
         InstrNr = Instr.InstrNr;  
         FormatFamily = Instr.FormatFamily;  
         FormatVersion = Instr.FormatVersion;  
         Size = Instr.Size;  
         Created = Instr.Created;  
         Modified = Instr.Modified;  
         Description = Instr.Description;  
         IsDrum = Instr.IsDrum;  
         Product = Instr.Product;  
         Artists = Instr.Artists;  
         Keywords = Instr.Keywords;  
     }  
   
   
     void DbDirectory::Copy(const DbDirectory& Dir) {  
         if (this == &Dir) return;  
37    
38          Created = Dir.Created;      void InstrumentsDb::CreateInstrumentsDb(String FilePath) {
39          Modified = Dir.Modified;          File f = File(FilePath);
40          Description = Dir.Description;          if (f.Exist()) {
41                throw Exception("File exists: " + FilePath);
42            }
43            
44            GetInstrumentsDb()->SetDbFile(FilePath);
45    
46            String sql =
47                "  CREATE TABLE instr_dirs (                                      "
48                "      dir_id         INTEGER PRIMARY KEY AUTOINCREMENT,          "
49                "      parent_dir_id  INTEGER DEFAULT 0,                          "
50                "      dir_name       TEXT,                                       "
51                "      created        TIMESTAMP DEFAULT CURRENT_TIMESTAMP,        "
52                "      modified       TIMESTAMP DEFAULT CURRENT_TIMESTAMP,        "
53                "      description    TEXT,                                       "
54                "      FOREIGN KEY(parent_dir_id) REFERENCES instr_dirs(dir_id),  "
55                "      UNIQUE (parent_dir_id,dir_name)                            "
56                "  );                                                             ";
57            
58            GetInstrumentsDb()->ExecSql(sql);
59    
60            sql = "INSERT INTO instr_dirs (dir_id, parent_dir_id, dir_name) VALUES (0, -2, '/');";
61            GetInstrumentsDb()->ExecSql(sql);
62    
63            sql =
64                "  CREATE TABLE instruments (                                "
65                "      instr_id        INTEGER PRIMARY KEY AUTOINCREMENT,    "
66                "      dir_id          INTEGER DEFAULT 0,                    "
67                "      instr_name      TEXT,                                 "
68                "      instr_file      TEXT,                                 "
69                "      instr_nr        INTEGER,                              "
70                "      format_family   TEXT,                                 "
71                "      format_version  TEXT,                                 "
72                "      instr_size      INTEGER,                              "
73                "      created         TIMESTAMP DEFAULT CURRENT_TIMESTAMP,  "
74                "      modified        TIMESTAMP DEFAULT CURRENT_TIMESTAMP,  "
75                "      description     TEXT,                                 "
76                "      is_drum         INTEGER(1),                           "
77                "      product         TEXT,                                 "
78                "      artists         TEXT,                                 "
79                "      keywords        TEXT,                                 "
80                "      FOREIGN KEY(dir_id) REFERENCES instr_dirs(dir_id),    "
81                "      UNIQUE (dir_id,instr_name)                            "
82                "  );                                                        ";
83            
84            GetInstrumentsDb()->ExecSql(sql);
85      }      }
86    
   
     InstrumentsDb* InstrumentsDb::pInstrumentsDb = new InstrumentsDb;  
   
87      InstrumentsDb::InstrumentsDb() {      InstrumentsDb::InstrumentsDb() {
88          db = NULL;          db = NULL;
89          DbInstrumentsMutex = Mutex();          DbInstrumentsMutex = Mutex();
90            InTransaction = false;
91      }      }
92    
93      InstrumentsDb::~InstrumentsDb() {      InstrumentsDb::~InstrumentsDb() {
94          if (db != NULL) sqlite3_close(db);          if (db != NULL) sqlite3_close(db);
95      }      }
96            
     void InstrumentsDb::Destroy() {  
         if (pInstrumentsDb != NULL) {  
             delete pInstrumentsDb;  
             pInstrumentsDb = NULL;  
         }  
     }  
   
97      void InstrumentsDb::AddInstrumentsDbListener(InstrumentsDb::Listener* l) {      void InstrumentsDb::AddInstrumentsDbListener(InstrumentsDb::Listener* l) {
98          llInstrumentsDbListeners.AddListener(l);          llInstrumentsDbListeners.AddListener(l);
99      }      }
# Line 85  namespace LinuxSampler { Line 103  namespace LinuxSampler {
103      }      }
104            
105      InstrumentsDb* InstrumentsDb::GetInstrumentsDb() {      InstrumentsDb* InstrumentsDb::GetInstrumentsDb() {
106          return pInstrumentsDb;          return &instance;
107      }      }
108            
109      void InstrumentsDb::SetDbFile(String File) {      void InstrumentsDb::SetDbFile(String File) {
# Line 101  namespace LinuxSampler { Line 119  namespace LinuxSampler {
119      sqlite3* InstrumentsDb::GetDb() {      sqlite3* InstrumentsDb::GetDb() {
120          if ( db != NULL) return db;          if ( db != NULL) return db;
121    
122          if (DbFile.empty()) DbFile = "/var/lib/linuxsampler/instruments.db";          if (DbFile.empty()) DbFile = CONFIG_DEFAULT_INSTRUMENTS_DB_LOCATION;
123                    #if defined(__APPLE__)  /* 20071224 Toshi Nagata  */
124                    if (DbFile.find("~") == 0)
125                            DbFile.replace(0, 1, getenv("HOME"));
126                    #endif
127          int rc = sqlite3_open(DbFile.c_str(), &db);          int rc = sqlite3_open(DbFile.c_str(), &db);
128          if (rc) {          if (rc) {
129              sqlite3_close(db);              sqlite3_close(db);
130              db = NULL;              db = NULL;
131              throw Exception("Cannot open instruments database: " + DbFile);              throw Exception("Cannot open instruments database: " + DbFile);
132          }          }
133            rc = sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8, NULL, Regexp, NULL, NULL);
134            if (rc) { throw Exception("Failed to add user function for handling regular expressions."); }
135    
136            // TODO: remove this in the next version
137            try {
138                int i = ExecSqlInt("SELECT parent_dir_id FROM instr_dirs WHERE dir_id=0");
139                // The parent ID of the root directory should be -2 now.
140                if(i != -2) ExecSql("UPDATE instr_dirs SET parent_dir_id=-2 WHERE dir_id=0");
141            } catch(Exception e) { }
142            ////////////////////////////////////////
143                    
144          return db;          return db;
145      }      }
# Line 121  namespace LinuxSampler { Line 153  namespace LinuxSampler {
153                    
154          int count = ExecSqlInt(sql.str());          int count = ExecSqlInt(sql.str());
155    
         // While the root dir has ID 0 and parent ID 0, the directory  
         // count for the root dir will be incorrect, so we should fix it.  
         if (count != -1 && DirId == 0) count--;  
156          return count;          return count;
157      }      }
158    
159      int InstrumentsDb::GetDirectoryCount(String Dir) {      int InstrumentsDb::GetDirectoryCount(String Dir, bool Recursive) {
160          dmsg(2,("InstrumentsDb: GetDirectoryCount(Dir=%s)\n", Dir.c_str()));          dmsg(2,("InstrumentsDb: GetDirectoryCount(Dir=%s,Recursive=%d)\n", Dir.c_str(), Recursive));
161          int i;          int i;
162    
163          DbInstrumentsMutex.Lock();          BeginTransaction();
164          try { i = GetDirectoryCount(GetDirectoryId(Dir)); }          try {
165          catch (Exception e) {              if (Recursive) {
166              DbInstrumentsMutex.Unlock();                  DirectoryCounter directoryCounter;
167                    DirectoryTreeWalk(Dir, &directoryCounter);
168                    i = directoryCounter.GetDirectoryCount();
169                } else {
170                    i = GetDirectoryCount(GetDirectoryId(Dir));
171                }
172            } catch (Exception e) {
173                EndTransaction();
174              throw e;              throw e;
175          }          }
176          DbInstrumentsMutex.Unlock();          EndTransaction();
177          if (i == -1) throw Exception("Unkown DB directory: " + Dir);          if (i == -1) throw Exception("Unkown DB directory: " + toEscapedPath(Dir));
178                    
179          return i;          return i;
180      }      }
# Line 151  namespace LinuxSampler { Line 187  namespace LinuxSampler {
187          return ExecSqlIntList(sql.str());          return ExecSqlIntList(sql.str());
188      }      }
189    
190      StringListPtr InstrumentsDb::GetDirectories(String Dir) {      StringListPtr InstrumentsDb::GetDirectories(String Dir, bool Recursive) {
191          dmsg(2,("InstrumentsDb: GetDirectories(Dir=%s)\n", Dir.c_str()));          dmsg(2,("InstrumentsDb: GetDirectories(Dir=%s,Recursive=%d)\n", Dir.c_str(), Recursive));
192    
193          DbInstrumentsMutex.Lock();          BeginTransaction();
194          try {          try {
195              int dirId = GetDirectoryId(Dir);              int dirId = GetDirectoryId(Dir);
196              if(dirId == -1) throw Exception("Unknown DB directory: " + Dir);              if(dirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
   
             std::stringstream sql;  
             sql << "SELECT dir_name FROM instr_dirs ";  
             sql << "WHERE parent_dir_id=" << dirId << " AND dir_id!=0";  
197    
198              DbInstrumentsMutex.Unlock();              StringListPtr pDirs;
199              return ExecSqlStringList(sql.str());              if (Recursive) {
200                    SearchQuery q;
201                    DirectoryFinder directoryFinder(&q);
202                    DirectoryTreeWalk(Dir, &directoryFinder);
203                    pDirs = directoryFinder.GetDirectories();
204                } else {
205                    pDirs = GetDirectories(dirId);
206                }
207                EndTransaction();
208                return pDirs;
209          } catch (Exception e) {          } catch (Exception e) {
210              DbInstrumentsMutex.Unlock();              EndTransaction();
211              throw e;              throw e;
212          }          }
213      }      }
214        
215        StringListPtr InstrumentsDb::GetDirectories(int DirId) {
216            std::stringstream sql;
217            sql << "SELECT dir_name FROM instr_dirs ";
218            sql << "WHERE parent_dir_id=" << DirId << " AND dir_id!=0";
219            StringListPtr dirs = ExecSqlStringList(sql.str());
220    
221            for (int i = 0; i < dirs->size(); i++) {
222                for (int j = 0; j < dirs->at(i).length(); j++) {
223                    if (dirs->at(i).at(j) == '/') dirs->at(i).at(j) = '\0';
224                }
225            }
226    
227            return dirs;
228        }
229    
230      int InstrumentsDb::GetDirectoryId(String Dir) {      int InstrumentsDb::GetDirectoryId(String Dir) {
231          dmsg(2,("InstrumentsDb: GetDirectoryId(Dir=%s)\n", Dir.c_str()));          dmsg(2,("InstrumentsDb: GetDirectoryId(Dir=%s)\n", Dir.c_str()));
# Line 197  namespace LinuxSampler { Line 253  namespace LinuxSampler {
253    
254      int InstrumentsDb::GetDirectoryId(int ParentDirId, String DirName) {      int InstrumentsDb::GetDirectoryId(int ParentDirId, String DirName) {
255          dmsg(2,("InstrumentsDb: GetDirectoryId(ParentDirId=%d, DirName=%s)\n", ParentDirId, DirName.c_str()));          dmsg(2,("InstrumentsDb: GetDirectoryId(ParentDirId=%d, DirName=%s)\n", ParentDirId, DirName.c_str()));
256            DirName = toDbName(DirName);
257          std::stringstream sql;          std::stringstream sql;
258          sql << "SELECT dir_id FROM instr_dirs WHERE parent_dir_id=";          sql << "SELECT dir_id FROM instr_dirs WHERE parent_dir_id=";
259          sql << ParentDirId << " AND dir_name=?";          sql << ParentDirId << " AND dir_name=?";
260          return ExecSqlInt(sql.str(), DirName);          return ExecSqlInt(sql.str(), DirName);
261      }      }
262    
263        String InstrumentsDb::GetDirectoryName(int DirId) {
264            String sql = "SELECT dir_name FROM instr_dirs WHERE dir_id=" + ToString(DirId);
265            String name = ExecSqlString(sql);
266            if (name.empty()) throw Exception("Directory ID not found");
267            return name;
268        }
269    
270        int InstrumentsDb::GetParentDirectoryId(int DirId) {
271            if (DirId == 0) throw Exception("The root directory is specified");
272            String sql = "SELECT parent_dir_id FROM instr_dirs WHERE dir_id=" + ToString(DirId);
273            int parentId = ExecSqlInt(sql);
274            if (parentId == -1) throw Exception("DB directory not found");
275            return parentId;
276        }
277    
278        String InstrumentsDb::GetDirectoryPath(int DirId) {
279            String path = "";
280            int count = 1000; // used to prevent infinite loops
281    
282            while(--count) {
283                if (DirId == 0) {
284                    path = "/" + path;
285                    break;
286                }
287                path = GetDirectoryName(DirId) + path;
288                DirId = GetParentDirectoryId(DirId);
289            }
290    
291            if (!count) throw Exception("Possible infinite loop detected");
292    
293            return path;
294        }
295    
296      void InstrumentsDb::AddDirectory(String Dir) {      void InstrumentsDb::AddDirectory(String Dir) {
297          dmsg(2,("InstrumentsDb: AddDirectory(Dir=%s)\n", Dir.c_str()));          dmsg(2,("InstrumentsDb: AddDirectory(Dir=%s)\n", Dir.c_str()));
298          CheckPathName(Dir);          CheckPathName(Dir);
299          String ParentDir = GetParentDirectory(Dir);          String ParentDir = GetParentDirectory(Dir);
300                    
301          DbInstrumentsMutex.Lock();          BeginTransaction();
302          try {          try {
303              if (Dir.length() > 1) {              if (Dir.length() > 1) {
304                  if (Dir.at(Dir.length() - 1) == '/') Dir.erase(Dir.length() - 1);                  if (Dir.at(Dir.length() - 1) == '/') Dir.erase(Dir.length() - 1);
# Line 216  namespace LinuxSampler { Line 306  namespace LinuxSampler {
306    
307              String dirName = GetFileName(Dir);              String dirName = GetFileName(Dir);
308              if(ParentDir.empty() || dirName.empty()) {              if(ParentDir.empty() || dirName.empty()) {
309                  throw Exception("Failed to add DB directory: " + Dir);                  throw Exception("Failed to add DB directory: " + toEscapedPath(Dir));
310              }              }
311    
312              int id = GetDirectoryId(ParentDir);              int id = GetDirectoryId(ParentDir);
313              if (id == -1) throw Exception("DB directory doesn't exist: " + ParentDir);              if (id == -1) throw Exception("DB directory doesn't exist: " + toEscapedPath(ParentDir));
314              int id2 = GetDirectoryId(id, dirName);              int id2 = GetDirectoryId(id, dirName);
315              if (id2 != -1) throw Exception("DB directory already exist: " + Dir);              if (id2 != -1) throw Exception("DB directory already exist: " + toEscapedPath(Dir));
316                id2 = GetInstrumentId(id, dirName);
317                if (id2 != -1) throw Exception("Instrument with that name exist: " + toEscapedPath(Dir));
318    
319              std::stringstream sql;              std::stringstream sql;
320              sql << "INSERT INTO instr_dirs (parent_dir_id, dir_name) VALUES (";              sql << "INSERT INTO instr_dirs (parent_dir_id, dir_name) VALUES (";
321              sql << id << ", ?)";              sql << id << ", ?)";
322    
323              ExecSql(sql.str(), dirName);              ExecSql(sql.str(), toDbName(dirName));
324          } catch (Exception e) {          } catch (Exception e) {
325              DbInstrumentsMutex.Unlock();              EndTransaction();
326              throw e;              throw e;
327          }          }
328    
329          DbInstrumentsMutex.Unlock();          EndTransaction();
330    
331          FireDirectoryCountChanged(ParentDir);          FireDirectoryCountChanged(ParentDir);
332      }      }
# Line 244  namespace LinuxSampler { Line 336  namespace LinuxSampler {
336    
337          String ParentDir = GetParentDirectory(Dir);          String ParentDir = GetParentDirectory(Dir);
338    
339          DbInstrumentsMutex.Lock();          BeginTransaction();
340          try {          try {
341              int dirId = GetDirectoryId(Dir);              int dirId = GetDirectoryId(Dir);
342              if (dirId == -1) throw Exception("Unknown DB directory: " + Dir);              if (dirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
343              if (dirId == 0) throw Exception("Cannot delete the root directory: " + Dir);              if (dirId == 0) throw Exception("Cannot delete the root directory: " + Dir);
344              if(ParentDir.empty()) throw Exception("Unknown parent directory");              if(ParentDir.empty()) throw Exception("Unknown parent directory");
345              if (Force) RemoveDirectoryContent(dirId);              if (Force) RemoveDirectoryContent(dirId);
346              RemoveDirectory(dirId);              RemoveDirectory(dirId);
347          } catch (Exception e) {          } catch (Exception e) {
348              DbInstrumentsMutex.Unlock();              EndTransaction();
349              throw e;              throw e;
350          }          }
351    
352          DbInstrumentsMutex.Unlock();          EndTransaction();
353          FireDirectoryCountChanged(ParentDir);          FireDirectoryCountChanged(ParentDir);
354      }      }
355    
# Line 330  namespace LinuxSampler { Line 422  namespace LinuxSampler {
422          dmsg(2,("InstrumentsDb: GetDirectoryInfo(Dir=%s)\n", Dir.c_str()));          dmsg(2,("InstrumentsDb: GetDirectoryInfo(Dir=%s)\n", Dir.c_str()));
423          DbDirectory d;          DbDirectory d;
424    
425          DbInstrumentsMutex.Lock();          BeginTransaction();
426    
427          try {          try {
428              int id = GetDirectoryId(Dir);              int id = GetDirectoryId(Dir);
429              if(id == -1) throw Exception("Unknown DB directory: " + Dir);              if(id == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
430    
431              sqlite3_stmt *pStmt = NULL;              sqlite3_stmt *pStmt = NULL;
432              std::stringstream sql;              std::stringstream sql;
# Line 357  namespace LinuxSampler { Line 449  namespace LinuxSampler {
449                  if (res != SQLITE_DONE) {                  if (res != SQLITE_DONE) {
450                      throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));                      throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
451                  } else {                  } else {
452                      throw Exception("Unknown DB directory: " + Dir);                      throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
453                  }                  }
454              }              }
455                            
456              sqlite3_finalize(pStmt);              sqlite3_finalize(pStmt);
457          } catch (Exception e) {          } catch (Exception e) {
458              DbInstrumentsMutex.Unlock();              EndTransaction();
459              throw e;              throw e;
460          }          }
461    
462          DbInstrumentsMutex.Unlock();          EndTransaction();
463          return d;          return d;
464      }      }
465    
466      void InstrumentsDb::RenameDirectory(String Dir, String Name) {      void InstrumentsDb::RenameDirectory(String Dir, String Name) {
467          dmsg(2,("InstrumentsDb: RenameDirectory(Dir=%s,Name=%s)\n", Dir.c_str(), Name.c_str()));          dmsg(2,("InstrumentsDb: RenameDirectory(Dir=%s,Name=%s)\n", Dir.c_str(), Name.c_str()));
468          CheckFileName(Name);          CheckFileName(Name);
469            String dbName = toDbName(Name);
470    
471          DbInstrumentsMutex.Lock();          BeginTransaction();
472          try {          try {
473              int dirId = GetDirectoryId(Dir);              int dirId = GetDirectoryId(Dir);
474              if (dirId == -1) throw Exception("Unknown DB directory: " + Dir);              if (dirId == -1) throw Exception("Unknown DB directory: " + toEscapedText(Dir));
475    
476              std::stringstream sql;              std::stringstream sql;
477              sql << "SELECT parent_dir_id FROM instr_dirs WHERE dir_id=" <<  dirId;              sql << "SELECT parent_dir_id FROM instr_dirs WHERE dir_id=" <<  dirId;
478    
479              int parent = ExecSqlInt(sql.str());              int parent = ExecSqlInt(sql.str());
480              if (parent == -1) throw Exception("Unknown parent directory: " + Dir);              if (parent == -1) throw Exception("Unknown parent directory: " + toEscapedPath(Dir));
481              if (GetDirectoryId(parent, Name) != -1) {  
482                  throw Exception("Cannot rename. Directory with that name already exists: " + Name);              if (GetDirectoryId(parent, dbName) != -1) {
483                    String s = toEscapedPath(Name);
484                    throw Exception("Cannot rename. Directory with that name already exists: " + s);
485                }
486    
487                if (GetInstrumentId(parent, dbName) != -1) {
488                    throw Exception("Cannot rename. Instrument with that name exist: " + toEscapedPath(Dir));
489              }              }
490    
491              sql.str("");              sql.str("");
492              sql << "UPDATE instr_dirs SET dir_name=? WHERE dir_id=" << dirId;              sql << "UPDATE instr_dirs SET dir_name=? WHERE dir_id=" << dirId;
493              ExecSql(sql.str(), Name);              ExecSql(sql.str(), dbName);
494          } catch (Exception e) {          } catch (Exception e) {
495              DbInstrumentsMutex.Unlock();              EndTransaction();
496              throw e;              throw e;
497          }          }
498    
499          DbInstrumentsMutex.Unlock();          EndTransaction();
500          FireDirectoryNameChanged(Dir, Name);          FireDirectoryNameChanged(Dir, toAbstractName(Name));
501      }      }
502    
503      void InstrumentsDb::MoveDirectory(String Dir, String Dst) {      void InstrumentsDb::MoveDirectory(String Dir, String Dst) {
504          dmsg(2,("InstrumentsDb: MoveDirectory(Dir=%s,Dst=%s)\n", Dir.c_str(), Dst.c_str()));          dmsg(2,("InstrumentsDb: MoveDirectory(Dir=%s,Dst=%s)\n", Dir.c_str(), Dst.c_str()));
505    
506            if(Dir.compare("/") == 0) throw Exception("Cannot move the root directory");
507          String ParentDir = GetParentDirectory(Dir);          String ParentDir = GetParentDirectory(Dir);
508          if(ParentDir.empty()) throw Exception("Unknown parent directory");          if(ParentDir.empty()) throw Exception("Unknown parent directory");
509    
510          DbInstrumentsMutex.Lock();          BeginTransaction();
511          try {          try {
512              int dirId = GetDirectoryId(Dir);              int dirId = GetDirectoryId(Dir);
513              if (dirId == -1) throw Exception("Unknown DB directory: " + Dir);              if (dirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
514              int dstId = GetDirectoryId(Dst);              int dstId = GetDirectoryId(Dst);
515              if (dstId == -1) throw Exception("Unknown DB directory: " + Dst);              if (dstId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dst));
516              if (dirId == dstId) {              if (dirId == dstId) {
517                  throw Exception("Cannot move directory to itself");                  throw Exception("Cannot move directory to itself");
518              }              }
# Line 423  namespace LinuxSampler { Line 523  namespace LinuxSampler {
523                      throw Exception("Cannot move a directory to a subdirectory of itself.");                      throw Exception("Cannot move a directory to a subdirectory of itself.");
524                  }                  }
525              }              }
526                
527                Dir.erase(Dir.length() - 1);
528                String dirName = GetFileName(Dir);
529    
530                int id2 = GetDirectoryId(dstId, dirName);
531                if (id2 != -1) throw Exception("DB directory already exist: " + toEscapedPath(dirName));
532                id2 = GetInstrumentId(dstId, dirName);
533                if (id2 != -1) throw Exception("Instrument with that name exist: " + toEscapedPath(dirName));
534    
535              std::stringstream sql;              std::stringstream sql;
536              sql << "UPDATE instr_dirs SET parent_dir_id=" << dstId;              sql << "UPDATE instr_dirs SET parent_dir_id=" << dstId;
537              sql << " WHERE dir_id=" << dirId;              sql << " WHERE dir_id=" << dirId;
538              ExecSql(sql.str());              ExecSql(sql.str());
539          } catch (Exception e) {          } catch (Exception e) {
540              DbInstrumentsMutex.Unlock();              EndTransaction();
541              throw e;              throw e;
542          }          }
543    
544          DbInstrumentsMutex.Unlock();          EndTransaction();
545          FireDirectoryCountChanged(ParentDir);          FireDirectoryCountChanged(ParentDir);
546          FireDirectoryCountChanged(Dst);          FireDirectoryCountChanged(Dst);
547      }      }
548    
549        void InstrumentsDb::CopyDirectory(String Dir, String Dst) {
550            dmsg(2,("InstrumentsDb: CopyDirectory(Dir=%s,Dst=%s)\n", Dir.c_str(), Dst.c_str()));
551    
552            if(Dir.compare("/") == 0) throw Exception("Cannot copy the root directory");
553            String ParentDir = GetParentDirectory(Dir);
554            if(ParentDir.empty()) throw Exception("Unknown parent directory");
555    
556            BeginTransaction();
557            try {
558                int dirId = GetDirectoryId(Dir);
559                if (dirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
560                int dstId = GetDirectoryId(Dst);
561                if (dstId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dst));
562                if (dirId == dstId) {
563                    throw Exception("Cannot copy directory to itself");
564                }
565    
566                if (Dir.at(Dir.length() - 1) != '/') Dir.append("/");
567                if (Dst.length() > Dir.length()) {
568                    if (Dir.compare(Dst.substr(0, Dir.length())) == 0) {
569                        throw Exception("Cannot copy a directory to a subdirectory of itself.");
570                    }
571                }
572                
573                Dir.erase(Dir.length() - 1);
574                String dirName = GetFileName(Dir);
575    
576                int id2 = GetDirectoryId(dstId, dirName);
577                if (id2 != -1) throw Exception("DB directory already exist: " + toEscapedPath(dirName));
578                id2 = GetInstrumentId(dstId, dirName);
579                if (id2 != -1) throw Exception("Instrument with that name exist: " + toEscapedPath(dirName));
580    
581                DirectoryCopier directoryCopier(ParentDir, Dst);
582                DirectoryTreeWalk(Dir, &directoryCopier);
583            } catch (Exception e) {
584                EndTransaction();
585                throw e;
586            }
587    
588            EndTransaction();
589        }
590    
591      void InstrumentsDb::SetDirectoryDescription(String Dir, String Desc) {      void InstrumentsDb::SetDirectoryDescription(String Dir, String Desc) {
592          dmsg(2,("InstrumentsDb: SetDirectoryDescription(Dir=%s,Desc=%s)\n", Dir.c_str(), Desc.c_str()));          dmsg(2,("InstrumentsDb: SetDirectoryDescription(Dir=%s,Desc=%s)\n", Dir.c_str(), Desc.c_str()));
593                    
594          DbInstrumentsMutex.Lock();          BeginTransaction();
595          try {          try {
596              int id = GetDirectoryId(Dir);              int id = GetDirectoryId(Dir);
597              if(id == -1) throw Exception("Unknown DB directory: " + Dir);              if(id == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
598    
599              std::stringstream sql;              std::stringstream sql;
600              sql << "UPDATE instr_dirs SET description=?,modified=CURRENT_TIMESTAMP ";              sql << "UPDATE instr_dirs SET description=?,modified=CURRENT_TIMESTAMP ";
# Line 452  namespace LinuxSampler { Line 602  namespace LinuxSampler {
602                    
603              ExecSql(sql.str(), Desc);              ExecSql(sql.str(), Desc);
604          } catch (Exception e) {          } catch (Exception e) {
605              DbInstrumentsMutex.Unlock();              EndTransaction();
606              throw e;              throw e;
607          }          }
608          DbInstrumentsMutex.Unlock();          EndTransaction();
609                    
610          FireDirectoryInfoChanged(Dir);          FireDirectoryInfoChanged(Dir);
611      }      }
612    
613      void InstrumentsDb::AddInstruments(String DbDir, String FilePath, int Index) {      int InstrumentsDb::AddInstruments(ScanMode Mode, String DbDir, String FsDir, bool bBackground) {
614            dmsg(2,("InstrumentsDb: AddInstruments(Mode=%d,DbDir=%s,FsDir=%s,bBackground=%d)\n", Mode, DbDir.c_str(), FsDir.c_str(), bBackground));
615            if(!bBackground) {
616                switch (Mode) {
617                    case NON_RECURSIVE:
618                        AddInstrumentsNonrecursive(DbDir, FsDir);
619                        break;
620                    case RECURSIVE:
621                        AddInstrumentsRecursive(DbDir, FsDir);
622                        break;
623                    case FLAT:
624                        AddInstrumentsRecursive(DbDir, FsDir, true);
625                        break;
626                    default:
627                        throw Exception("Unknown scan mode");
628                }
629    
630                return -1;
631            }
632    
633            ScanJob job;
634            int jobId = Jobs.AddJob(job);
635            InstrumentsDbThread.Execute(new AddInstrumentsJob(jobId, Mode, DbDir, FsDir));
636    
637            return jobId;
638        }
639        
640        int InstrumentsDb::AddInstruments(String DbDir, String FilePath, int Index, bool bBackground) {
641            dmsg(2,("InstrumentsDb: AddInstruments(DbDir=%s,FilePath=%s,Index=%d,bBackground=%d)\n", DbDir.c_str(), FilePath.c_str(), Index, bBackground));
642            if(!bBackground) {
643                AddInstruments(DbDir, FilePath, Index);
644                return -1;
645            }
646    
647            ScanJob job;
648            int jobId = Jobs.AddJob(job);
649            InstrumentsDbThread.Execute(new AddInstrumentsFromFileJob(jobId, DbDir, FilePath, Index));
650    
651            return jobId;
652        }
653    
654        void InstrumentsDb::AddInstruments(String DbDir, String FilePath, int Index, ScanProgress* pProgress) {
655          dmsg(2,("InstrumentsDb: AddInstruments(DbDir=%s,FilePath=%s,Index=%d)\n", DbDir.c_str(), FilePath.c_str(), Index));          dmsg(2,("InstrumentsDb: AddInstruments(DbDir=%s,FilePath=%s,Index=%d)\n", DbDir.c_str(), FilePath.c_str(), Index));
656          if (DbDir.empty() || FilePath.empty()) return;          if (DbDir.empty() || FilePath.empty()) return;
657                    
658          DbInstrumentsMutex.Lock();          DbInstrumentsMutex.Lock();
659          try {          try {
660              int dirId = GetDirectoryId(DbDir);              int dirId = GetDirectoryId(DbDir);
661              if (dirId == -1) throw Exception("Invalid DB directory: " + DbDir);              if (dirId == -1) throw Exception("Invalid DB directory: " + toEscapedText(DbDir));
662    
663              struct stat statBuf;              File f = File(FilePath);
664              int res = stat(FilePath.c_str(), &statBuf);              if (!f.Exist()) {
             if (res) {  
665                  std::stringstream ss;                  std::stringstream ss;
666                  ss << "Fail to stat `" << FilePath << "`: " << strerror(errno);                  ss << "Fail to stat `" << FilePath << "`: " << f.GetErrorMsg();
667                  throw Exception(ss.str());                  throw Exception(ss.str());
668              }              }
669    
670              if (S_ISREG(statBuf.st_mode)) {              if (!f.IsFile()) {
                 AddInstrumentsFromFile(DbDir, FilePath, Index);  
                 DbInstrumentsMutex.Unlock();  
                 return;  
             }  
   
             if (!S_ISDIR(statBuf.st_mode)) {  
                 DbInstrumentsMutex.Unlock();  
                 return;  
             }  
               
             if (Index != -1) {  
671                  std::stringstream ss;                  std::stringstream ss;
672                  ss << "`" << FilePath << "` is directory, not an instrument file";                  ss << "`" << FilePath << "` is not an instrument file";
673                  throw Exception(ss.str());                  throw Exception(ss.str());
674              }              }
675            
676              AddInstrumentsRecursive(DbDir, FilePath, false);              AddInstrumentsFromFile(DbDir, FilePath, Index, pProgress);
677          } catch (Exception e) {          } catch (Exception e) {
678              DbInstrumentsMutex.Unlock();              DbInstrumentsMutex.Unlock();
679              throw e;              throw e;
# Line 503  namespace LinuxSampler { Line 682  namespace LinuxSampler {
682          DbInstrumentsMutex.Unlock();          DbInstrumentsMutex.Unlock();
683      }      }
684    
685      void InstrumentsDb::AddInstrumentsNonrecursive(String DbDir, String FsDir) {      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()));          dmsg(2,("InstrumentsDb: AddInstrumentsNonrecursive(DbDir=%s,FsDir=%s)\n", DbDir.c_str(), FsDir.c_str()));
687          if (DbDir.empty() || FsDir.empty()) return;          if (DbDir.empty() || FsDir.empty()) return;
688                    
689          DbInstrumentsMutex.Lock();          DbInstrumentsMutex.Lock();
690          try {          try {
691              int dirId = GetDirectoryId(DbDir);              int dirId = GetDirectoryId(DbDir);
692              if (dirId == -1) throw Exception("Invalid DB directory: " + DbDir);              if (dirId == -1) throw Exception("Invalid DB directory: " + toEscapedPath(DbDir));
693    
694              struct stat statBuf;              File f = File(FsDir);
695              int res = stat(FsDir.c_str(), &statBuf);              if (!f.Exist()) {
             if (res) {  
696                  std::stringstream ss;                  std::stringstream ss;
697                  ss << "Fail to stat `" << FsDir << "`: " << strerror(errno);                  ss << "Fail to stat `" << FsDir << "`: " << f.GetErrorMsg();
698                  throw Exception(ss.str());                  throw Exception(ss.str());
699              }              }
700    
701              if (!S_ISDIR(statBuf.st_mode)) {              if (!f.IsDirectory()) {
702                  throw Exception("Directory expected");                  throw Exception("Directory expected: " + FsDir);
703              }              }
704                            
705              if (FsDir.at(FsDir.length() - 1) != '/') FsDir.append("/");              if (FsDir.at(FsDir.length() - 1) != File::DirSeparator) {
706                    FsDir.push_back(File::DirSeparator);
             DIR* pDir = opendir(FsDir.c_str());  
             if (pDir == NULL) {  
                 std::stringstream ss;  
                 ss << "The scanning of directory `" << FsDir << "` failed: ";  
                 ss << strerror(errno);  
                 std::cout << ss.str();  
                 DbInstrumentsMutex.Unlock();  
                 return;  
707              }              }
708                
709              struct dirent* pEnt = readdir(pDir);              try {
710              while (pEnt != NULL) {                  FileListPtr fileList = File::GetFiles(FsDir);
711                  if (pEnt->d_type != DT_REG) {                  for (int i = 0; i < fileList->size(); i++) {
712                      pEnt = readdir(pDir);                      AddInstrumentsFromFile(DbDir, FsDir + fileList->at(i), -1, pProgress);
                     continue;  
713                  }                  }
714                } catch(Exception e) {
715                  AddInstrumentsFromFile(DbDir, FsDir + String(pEnt->d_name));                  e.PrintMessage();
716                  pEnt = readdir(pDir);                  DbInstrumentsMutex.Unlock();
717              }                  return;
   
             if (closedir(pDir)) {  
                 std::stringstream ss;  
                 ss << "Failed to close directory `" << FsDir << "`: ";  
                 ss << strerror(errno);  
                 std::cout << ss.str();  
718              }              }
719          } catch (Exception e) {          } catch (Exception e) {
720              DbInstrumentsMutex.Unlock();              DbInstrumentsMutex.Unlock();
# Line 561  namespace LinuxSampler { Line 724  namespace LinuxSampler {
724          DbInstrumentsMutex.Unlock();          DbInstrumentsMutex.Unlock();
725      }      }
726    
727      void InstrumentsDb::AddInstrumentsRecursive(String DbDir, String FsDir, bool Flat) {      void InstrumentsDb::AddInstrumentsRecursive(String DbDir, String FsDir, bool Flat, ScanProgress* pProgress) {
728          dmsg(2,("InstrumentsDb: AddInstrumentsRecursive(DbDir=%s,FsDir=%s,Flat=%d)\n", DbDir.c_str(), FsDir.c_str(), Flat));          dmsg(2,("InstrumentsDb: AddInstrumentsRecursive(DbDir=%s,FsDir=%s,Flat=%d)\n", DbDir.c_str(), FsDir.c_str(), Flat));
729          DirectoryScanner::Scan(DbDir, FsDir, Flat);          if (pProgress != NULL) {
730                InstrumentFileCounter c;
731                pProgress->SetTotalFileCount(c.Count(FsDir));
732            }
733    
734            DirectoryScanner d;
735            d.Scan(DbDir, FsDir, Flat, pProgress);
736      }      }
737    
738      int InstrumentsDb::GetInstrumentCount(int DirId) {      int InstrumentsDb::GetInstrumentCount(int DirId) {
# Line 576  namespace LinuxSampler { Line 745  namespace LinuxSampler {
745          return ExecSqlInt(sql.str());          return ExecSqlInt(sql.str());
746      }      }
747    
748      int InstrumentsDb::GetInstrumentCount(String Dir) {      int InstrumentsDb::GetInstrumentCount(String Dir, bool Recursive) {
749          dmsg(2,("InstrumentsDb: GetInstrumentCount(Dir=%s)\n", Dir.c_str()));          dmsg(2,("InstrumentsDb: GetInstrumentCount(Dir=%s,Recursive=%d)\n", Dir.c_str(), Recursive));
750          int i;          int i;
751                    
752          DbInstrumentsMutex.Lock();          BeginTransaction();
753          try { i = GetInstrumentCount(GetDirectoryId(Dir)); }          try {
754          catch (Exception e) {              if (Recursive) {
755              DbInstrumentsMutex.Unlock();                  InstrumentCounter instrumentCounter;
756                    DirectoryTreeWalk(Dir, &instrumentCounter);
757                    i = instrumentCounter.GetInstrumentCount();
758                } else {
759                    i = GetInstrumentCount(GetDirectoryId(Dir));
760                }
761            } catch (Exception e) {
762                EndTransaction();
763              throw e;              throw e;
764          }          }
765          DbInstrumentsMutex.Unlock();          EndTransaction();
766    
767          if (i == -1) throw Exception("Unknown Db directory: " + Dir);          if (i == -1) throw Exception("Unknown Db directory: " + toEscapedPath(Dir));
768          return i;          return i;
769      }      }
770    
# Line 599  namespace LinuxSampler { Line 775  namespace LinuxSampler {
775          return ExecSqlIntList(sql.str());          return ExecSqlIntList(sql.str());
776      }      }
777    
778      StringListPtr InstrumentsDb::GetInstruments(String Dir) {      StringListPtr InstrumentsDb::GetInstruments(String Dir, bool Recursive) {
779          dmsg(2,("InstrumentsDb: GetInstruments(Dir=%s)\n", Dir.c_str()));          dmsg(2,("InstrumentsDb: GetInstruments(Dir=%s,Recursive=%d)\n", Dir.c_str(), Recursive));
780          DbInstrumentsMutex.Lock();          BeginTransaction();
781          try {          try {
782              int dirId = GetDirectoryId(Dir);              int dirId = GetDirectoryId(Dir);
783              if(dirId == -1) throw Exception("Unknown DB directory: " + Dir);              if(dirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
784    
785              std::stringstream sql;              StringListPtr pInstrs;
             sql << "SELECT instr_name FROM instruments WHERE dir_id=" << dirId;  
786    
787              StringListPtr instrs = ExecSqlStringList(sql.str());              if(Recursive) {
788              DbInstrumentsMutex.Unlock();                  SearchQuery q;
789              return instrs;                  InstrumentFinder instrumentFinder(&q);
790                    DirectoryTreeWalk(Dir, &instrumentFinder);
791                    pInstrs = instrumentFinder.GetInstruments();
792                } else {
793                    std::stringstream sql;
794                    sql << "SELECT instr_name FROM instruments WHERE dir_id=" << dirId;
795    
796                    pInstrs = ExecSqlStringList(sql.str());
797                    // Converting to abstract names
798                    for (int i = 0; i < pInstrs->size(); i++) {
799                        for (int j = 0; j < pInstrs->at(i).length(); j++) {
800                            if (pInstrs->at(i).at(j) == '/') pInstrs->at(i).at(j) = '\0';
801                        }
802                    }
803                }
804                EndTransaction();
805                return pInstrs;
806          } catch (Exception e) {          } catch (Exception e) {
807              DbInstrumentsMutex.Unlock();              EndTransaction();
808              throw e;              throw e;
809          }          }
810      }      }
# Line 633  namespace LinuxSampler { Line 824  namespace LinuxSampler {
824          std::stringstream sql;          std::stringstream sql;
825          sql << "SELECT instr_id FROM instruments WHERE dir_id=";          sql << "SELECT instr_id FROM instruments WHERE dir_id=";
826          sql << DirId << " AND instr_name=?";          sql << DirId << " AND instr_name=?";
827          return ExecSqlInt(sql.str(), InstrName);          return ExecSqlInt(sql.str(), toDbName(InstrName));
828        }
829    
830        String InstrumentsDb::GetInstrumentName(int InstrId) {
831            dmsg(2,("InstrumentsDb: GetInstrumentName(InstrId=%d)\n", InstrId));
832            std::stringstream sql;
833            sql << "SELECT instr_name FROM instruments WHERE instr_id=" << InstrId;
834            return toAbstractName(ExecSqlString(sql.str()));
835      }      }
836            
837      void InstrumentsDb::RemoveInstrument(String Instr) {      void InstrumentsDb::RemoveInstrument(String Instr) {
# Line 641  namespace LinuxSampler { Line 839  namespace LinuxSampler {
839          String ParentDir = GetDirectoryPath(Instr);          String ParentDir = GetDirectoryPath(Instr);
840          if(ParentDir.empty()) throw Exception("Unknown parent directory");          if(ParentDir.empty()) throw Exception("Unknown parent directory");
841                    
842          DbInstrumentsMutex.Lock();          BeginTransaction();
843          try {          try {
844              int instrId = GetInstrumentId(Instr);              int instrId = GetInstrumentId(Instr);
845              if(instrId == -1) {              if(instrId == -1) {
846                  throw Exception("The specified instrument does not exist: " + Instr);                  throw Exception("The specified instrument does not exist: " + toEscapedPath(Instr));
847              }              }
848              RemoveInstrument(instrId);              RemoveInstrument(instrId);
849          } catch (Exception e) {          } catch (Exception e) {
850              DbInstrumentsMutex.Unlock();              EndTransaction();
851              throw e;              throw e;
852          }          }
853          DbInstrumentsMutex.Unlock();          EndTransaction();
854          FireInstrumentCountChanged(ParentDir);          FireInstrumentCountChanged(ParentDir);
855      }      }
856    
# Line 677  namespace LinuxSampler { Line 875  namespace LinuxSampler {
875          dmsg(2,("InstrumentsDb: GetInstrumentInfo(Instr=%s)\n", Instr.c_str()));          dmsg(2,("InstrumentsDb: GetInstrumentInfo(Instr=%s)\n", Instr.c_str()));
876          DbInstrument i;          DbInstrument i;
877                    
878          DbInstrumentsMutex.Lock();          BeginTransaction();
879          try {          try {
880              int id = GetInstrumentId(Instr);              int id = GetInstrumentId(Instr);
881              if(id == -1) throw Exception("Unknown DB instrument: " + Instr);              if(id == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
882                i = GetInstrumentInfo(id);
883            } catch (Exception e) {
884                EndTransaction();
885                throw e;
886            }
887            EndTransaction();
888                    
889              sqlite3_stmt *pStmt = NULL;          return i;
890              std::stringstream sql;      }
             sql << "SELECT instr_file,instr_nr,format_family,format_version,";  
             sql << "instr_size,created,modified,description,is_drum,product,";  
             sql << "artists,keywords FROM instruments WHERE instr_id=" << id;  
891    
892              int res = sqlite3_prepare(GetDb(), sql.str().c_str(), -1, &pStmt, NULL);      DbInstrument InstrumentsDb::GetInstrumentInfo(int InstrId) {
893              if (res != SQLITE_OK) {          sqlite3_stmt *pStmt = NULL;
894                  throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));          std::stringstream sql;
895              }          sql << "SELECT instr_file,instr_nr,format_family,format_version,";
896            sql << "instr_size,created,modified,description,is_drum,product,";
897            sql << "artists,keywords FROM instruments WHERE instr_id=" << InstrId;
898    
899              res = sqlite3_step(pStmt);          int res = sqlite3_prepare(GetDb(), sql.str().c_str(), -1, &pStmt, NULL);
900              if(res == SQLITE_ROW) {          if (res != SQLITE_OK) {
901                  i.InstrFile = ToString(sqlite3_column_text(pStmt, 0));              throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
902                  i.InstrNr = sqlite3_column_int(pStmt, 1);          }
                 i.FormatFamily = ToString(sqlite3_column_text(pStmt, 2));  
                 i.FormatVersion = ToString(sqlite3_column_text(pStmt, 3));  
                 i.Size = sqlite3_column_int64(pStmt, 4);  
                 i.Created = ToString(sqlite3_column_text(pStmt, 5));  
                 i.Modified = ToString(sqlite3_column_text(pStmt, 6));  
                 i.Description = ToString(sqlite3_column_text(pStmt, 7));  
                 i.IsDrum = sqlite3_column_int(pStmt, 8);  
                 i.Product = ToString(sqlite3_column_text(pStmt, 9));  
                 i.Artists = ToString(sqlite3_column_text(pStmt, 10));  
                 i.Keywords = ToString(sqlite3_column_text(pStmt, 11));  
             } else {  
                 sqlite3_finalize(pStmt);  
               
                 if (res != SQLITE_DONE) {  
                     throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));  
                 } else {  
                     throw Exception("Unknown DB instrument: " + Instr);  
                 }  
             }  
903    
904            DbInstrument i;
905            res = sqlite3_step(pStmt);
906            if(res == SQLITE_ROW) {
907                i.InstrFile = ToString(sqlite3_column_text(pStmt, 0));
908                i.InstrNr = sqlite3_column_int(pStmt, 1);
909                i.FormatFamily = ToString(sqlite3_column_text(pStmt, 2));
910                i.FormatVersion = ToString(sqlite3_column_text(pStmt, 3));
911                i.Size = sqlite3_column_int64(pStmt, 4);
912                i.Created = ToString(sqlite3_column_text(pStmt, 5));
913                i.Modified = ToString(sqlite3_column_text(pStmt, 6));
914                i.Description = ToString(sqlite3_column_text(pStmt, 7));
915                i.IsDrum = sqlite3_column_int(pStmt, 8);
916                i.Product = ToString(sqlite3_column_text(pStmt, 9));
917                i.Artists = ToString(sqlite3_column_text(pStmt, 10));
918                i.Keywords = ToString(sqlite3_column_text(pStmt, 11));
919            } else {
920              sqlite3_finalize(pStmt);              sqlite3_finalize(pStmt);
921          } catch (Exception e) {  
922              DbInstrumentsMutex.Unlock();              if (res != SQLITE_DONE) {
923              throw e;                  throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
924                } else {
925                    throw Exception("Unknown DB instrument");
926                }
927          }          }
928          DbInstrumentsMutex.Unlock();  
929                    sqlite3_finalize(pStmt);
930          return i;          return i;
931      }      }
932    
# Line 731  namespace LinuxSampler { Line 934  namespace LinuxSampler {
934          dmsg(2,("InstrumentsDb: RenameInstrument(Instr=%s,Name=%s)\n", Instr.c_str(), Name.c_str()));          dmsg(2,("InstrumentsDb: RenameInstrument(Instr=%s,Name=%s)\n", Instr.c_str(), Name.c_str()));
935          CheckFileName(Name);          CheckFileName(Name);
936    
937          DbInstrumentsMutex.Lock();          BeginTransaction();
938          try {          try {
939              int dirId = GetDirectoryId(GetDirectoryPath(Instr));              int dirId = GetDirectoryId(GetDirectoryPath(Instr));
940              if (dirId == -1) throw Exception("Unknown DB instrument: " + Instr);              if (dirId == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
941    
942              int instrId = GetInstrumentId(dirId, GetFileName(Instr));              int instrId = GetInstrumentId(dirId, GetFileName(Instr));
943              if (instrId == -1) throw Exception("Unknown DB instrument: " + Instr);              if (instrId == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
944    
945              if (GetInstrumentId(dirId, Name) != -1) {              if (GetInstrumentId(dirId, Name) != -1) {
946                  throw Exception("Cannot rename. Instrument with that name already exists: " + Name);                  String s = toEscapedPath(Name);
947                    throw Exception("Cannot rename. Instrument with that name already exists: " + s);
948                }
949    
950                if (GetDirectoryId(dirId, Name) != -1) {
951                    String s = toEscapedPath(Name);
952                    throw Exception("Cannot rename. Directory with that name already exists: " + s);
953              }              }
954    
955              std::stringstream sql;              std::stringstream sql;
956              sql << "UPDATE instruments SET instr_name=? WHERE instr_id=" << instrId;              sql << "UPDATE instruments SET instr_name=? WHERE instr_id=" << instrId;
957              ExecSql(sql.str(), Name);              ExecSql(sql.str(), toDbName(Name));
958          } catch (Exception e) {          } catch (Exception e) {
959              DbInstrumentsMutex.Unlock();              EndTransaction();
960              throw e;              throw e;
961          }          }
962          DbInstrumentsMutex.Unlock();          EndTransaction();
963          FireInstrumentNameChanged(Instr, Name);          FireInstrumentNameChanged(Instr, toAbstractName(Name));
964      }      }
965    
966      void InstrumentsDb::MoveInstrument(String Instr, String Dst) {      void InstrumentsDb::MoveInstrument(String Instr, String Dst) {
# Line 759  namespace LinuxSampler { Line 968  namespace LinuxSampler {
968          String ParentDir = GetDirectoryPath(Instr);          String ParentDir = GetDirectoryPath(Instr);
969          if(ParentDir.empty()) throw Exception("Unknown parent directory");          if(ParentDir.empty()) throw Exception("Unknown parent directory");
970    
971          DbInstrumentsMutex.Lock();          BeginTransaction();
972          try {          try {
973              int dirId = GetDirectoryId(GetDirectoryPath(Instr));              int dirId = GetDirectoryId(ParentDir);
974              if (dirId == -1) throw Exception("Unknown DB instrument: " + Instr);              if (dirId == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
975    
976              String instrName = GetFileName(Instr);              String instrName = GetFileName(Instr);
977              int instrId = GetInstrumentId(dirId, instrName);              int instrId = GetInstrumentId(dirId, instrName);
978              if (instrId == -1) throw Exception("Unknown DB instrument: " + Instr);              if (instrId == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
979    
980              int dstId = GetDirectoryId(Dst);              int dstId = GetDirectoryId(Dst);
981              if (dstId == -1) throw Exception("Unknown DB directory: " + Dst);              if (dstId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dst));
982              if (dirId == dstId) {              if (dirId == dstId) {
983                  DbInstrumentsMutex.Unlock();                  EndTransaction();
984                  return;                  return;
985              }              }
986    
987              if (GetInstrumentId(dstId, instrName) != -1) {              if (GetInstrumentId(dstId, instrName) != -1) {
988                  throw Exception("Cannot move. Instrument with that name already exists: " + instrName);                  String s = toEscapedPath(instrName);
989                    throw Exception("Cannot move. Instrument with that name already exists: " + s);
990                }
991    
992                if (GetDirectoryId(dstId, instrName) != -1) {
993                    String s = toEscapedPath(instrName);
994                    throw Exception("Cannot move. Directory with that name already exists: " + s);
995              }              }
996    
997              std::stringstream sql;              std::stringstream sql;
# Line 784  namespace LinuxSampler { Line 999  namespace LinuxSampler {
999              sql << " WHERE instr_id=" << instrId;              sql << " WHERE instr_id=" << instrId;
1000              ExecSql(sql.str());              ExecSql(sql.str());
1001          } catch (Exception e) {          } catch (Exception e) {
1002              DbInstrumentsMutex.Unlock();              EndTransaction();
1003              throw e;              throw e;
1004          }          }
1005          DbInstrumentsMutex.Unlock();          EndTransaction();
1006          FireInstrumentCountChanged(ParentDir);          FireInstrumentCountChanged(ParentDir);
1007          FireInstrumentCountChanged(Dst);          FireInstrumentCountChanged(Dst);
1008      }      }
1009    
1010        void InstrumentsDb::CopyInstrument(String Instr, String Dst) {
1011            dmsg(2,("InstrumentsDb: CopyInstrument(Instr=%s,Dst=%s)\n", Instr.c_str(), Dst.c_str()));
1012            String ParentDir = GetDirectoryPath(Instr);
1013            if(ParentDir.empty()) throw Exception("Unknown parent directory");
1014    
1015            BeginTransaction();
1016            try {
1017                int dirId = GetDirectoryId(GetDirectoryPath(Instr));
1018                if (dirId == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
1019    
1020                String instrName = GetFileName(Instr);
1021                int instrId = GetInstrumentId(dirId, instrName);
1022                if (instrId == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
1023    
1024                int dstId = GetDirectoryId(Dst);
1025                if (dstId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dst));
1026                if (dirId == dstId) {
1027                    EndTransaction();
1028                    return;
1029                }
1030    
1031                CopyInstrument(instrId, instrName, dstId, Dst);
1032            } catch (Exception e) {
1033                EndTransaction();
1034                throw e;
1035            }
1036            EndTransaction();
1037            
1038        }
1039    
1040        void InstrumentsDb::CopyInstrument(int InstrId, String InstrName, int DstDirId, String DstDir) {
1041            if (GetInstrumentId(DstDirId, InstrName) != -1) {
1042                String s = toEscapedPath(InstrName);
1043                throw Exception("Cannot copy. Instrument with that name already exists: " + s);
1044            }
1045    
1046            if (GetDirectoryId(DstDirId, InstrName) != -1) {
1047                String s = toEscapedPath(InstrName);
1048                throw Exception("Cannot copy. Directory with that name already exists: " + s);
1049            }
1050    
1051            DbInstrument i = GetInstrumentInfo(InstrId);
1052            sqlite3_stmt *pStmt = NULL;
1053            std::stringstream sql;
1054            sql << "INSERT INTO instruments (dir_id,instr_name,instr_file,instr_nr,format_family,";
1055            sql << "format_version,instr_size,description,is_drum,product,artists,keywords) ";
1056            sql << "VALUES (" << DstDirId << ",?,?," << i.InstrNr << ",?,?," << i.Size << ",?,";
1057            sql << i.IsDrum << ",?,?,?)";
1058    
1059            int res = sqlite3_prepare(GetDb(), sql.str().c_str(), -1, &pStmt, NULL);
1060            if (res != SQLITE_OK) {
1061                throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1062            }
1063    
1064            String s = toDbName(InstrName);
1065            BindTextParam(pStmt, 1, s);
1066            BindTextParam(pStmt, 2, i.InstrFile);
1067            BindTextParam(pStmt, 3, i.FormatFamily);
1068            BindTextParam(pStmt, 4, i.FormatVersion);
1069            BindTextParam(pStmt, 5, i.Description);
1070            BindTextParam(pStmt, 6, i.Product);
1071            BindTextParam(pStmt, 7, i.Artists);
1072            BindTextParam(pStmt, 8, i.Keywords);
1073    
1074            res = sqlite3_step(pStmt);
1075            if(res != SQLITE_DONE) {
1076                sqlite3_finalize(pStmt);
1077                throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1078            }
1079            
1080            sqlite3_finalize(pStmt);
1081            FireInstrumentCountChanged(DstDir);
1082        }
1083    
1084      void InstrumentsDb::SetInstrumentDescription(String Instr, String Desc) {      void InstrumentsDb::SetInstrumentDescription(String Instr, String Desc) {
1085          dmsg(2,("InstrumentsDb: SetInstrumentDescription(Instr=%s,Desc=%s)\n", Instr.c_str(), Desc.c_str()));          dmsg(2,("InstrumentsDb: SetInstrumentDescription(Instr=%s,Desc=%s)\n", Instr.c_str(), Desc.c_str()));
1086    
1087          DbInstrumentsMutex.Lock();          BeginTransaction();
1088          try {          try {
1089              int id = GetInstrumentId(Instr);              int id = GetInstrumentId(Instr);
1090              if(id == -1) throw Exception("Unknown DB instrument: " + Instr);              if(id == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
1091    
1092              std::stringstream sql;              std::stringstream sql;
1093              sql << "UPDATE instruments SET description=?,modified=CURRENT_TIMESTAMP ";              sql << "UPDATE instruments SET description=?,modified=CURRENT_TIMESTAMP ";
# Line 806  namespace LinuxSampler { Line 1095  namespace LinuxSampler {
1095    
1096              ExecSql(sql.str(), Desc);              ExecSql(sql.str(), Desc);
1097          } catch (Exception e) {          } catch (Exception e) {
1098              DbInstrumentsMutex.Unlock();              EndTransaction();
1099              throw e;              throw e;
1100          }          }
1101          DbInstrumentsMutex.Unlock();          EndTransaction();
1102          FireInstrumentInfoChanged(Instr);          FireInstrumentInfoChanged(Instr);
1103      }      }
1104    
1105      void InstrumentsDb::AddInstrumentsFromFile(String DbDir, String File, int Index) {      void InstrumentsDb::AddInstrumentsFromFile(String DbDir, String File, int Index, ScanProgress* pProgress) {
1106          dmsg(2,("InstrumentsDb: AddInstrumentsFromFile(DbDir=%s,File=%s,Index=%d)\n", DbDir.c_str(), File.c_str(), Index));          dmsg(2,("InstrumentsDb: AddInstrumentsFromFile(DbDir=%s,File=%s,Index=%d)\n", DbDir.c_str(), File.c_str(), Index));
1107                    
1108          if(File.length() < 4) return;          if(File.length() < 4) return;
1109                    
1110          try {          try {
1111              if(!strcasecmp(".gig", File.substr(File.length() - 4).c_str())) {              if(!strcasecmp(".gig", File.substr(File.length() - 4).c_str())) {
1112                  AddGigInstruments(DbDir, File, Index);                  if (pProgress != NULL) {
1113                        pProgress->SetStatus(0);
1114                        pProgress->CurrentFile = File;
1115                    }
1116    
1117                    AddGigInstruments(DbDir, File, Index, pProgress);
1118    
1119                    if (pProgress != NULL) {
1120                        pProgress->SetScannedFileCount(pProgress->GetScannedFileCount() + 1);
1121                    }
1122              }              }
1123          } catch(Exception e) {          } catch(Exception e) {
1124              std::cout << e.Message() << std::endl;              e.PrintMessage();
1125          }          }
1126      }      }
1127    
1128      void InstrumentsDb::AddGigInstruments(String DbDir, String File, int Index) {      void InstrumentsDb::AddGigInstruments(String DbDir, String FilePath, int Index, ScanProgress* pProgress) {
1129          dmsg(2,("InstrumentsDb: AddGigInstruments(DbDir=%s,File=%s,Index=%d)\n", DbDir.c_str(), File.c_str(), Index));          dmsg(2,("InstrumentsDb: AddGigInstruments(DbDir=%s,FilePath=%s,Index=%d)\n", DbDir.c_str(), FilePath.c_str(), Index));
1130          int dirId = GetDirectoryId(DbDir);          int dirId = GetDirectoryId(DbDir);
1131          if (dirId == -1) throw Exception("Invalid DB directory: " + DbDir);          if (dirId == -1) throw Exception("Invalid DB directory: " + toEscapedPath(DbDir));
1132    
1133          struct stat statBuf;          File f = File(FilePath);
1134          int res = stat(File.c_str(), &statBuf);          if (!f.Exist()) {
         if (res) {  
1135              std::stringstream ss;              std::stringstream ss;
1136              ss << "Fail to stat `" << File << "`: " << strerror(errno);              ss << "Fail to stat `" << FilePath << "`: " << f.GetErrorMsg();
1137              throw Exception(ss.str());              throw Exception(ss.str());
1138          }          }
1139    
1140          if (!S_ISREG(statBuf.st_mode)) {          if (!f.IsFile()) {
1141              std::stringstream ss;              std::stringstream ss;
1142              ss << "`" << File << "` is not a regular file";              ss << "`" << FilePath << "` is not a regular file";
1143              throw Exception(ss.str());              throw Exception(ss.str());
1144          }          }
1145    
1146          RIFF::File* riff = NULL;          RIFF::File* riff = NULL;
1147          gig::File* gig = NULL;          gig::File* gig = NULL;
1148          try {          try {
1149              riff = new RIFF::File(File);              riff = new RIFF::File(FilePath);
1150              gig::File* gig = new gig::File(riff);              gig::File* gig = new gig::File(riff);
1151                            gig->SetAutoLoad(false); // avoid time consuming samples scanning
1152    
1153              std::stringstream sql;              std::stringstream sql;
1154              sql << "INSERT INTO instruments (dir_id,instr_name,instr_file,";              sql << "INSERT INTO instruments (dir_id,instr_name,instr_file,";
1155              sql << "instr_nr,format_family,format_version,instr_size,";              sql << "instr_nr,format_family,format_version,instr_size,";
1156              sql << "description,is_drum,product,artists,keywords) VALUES (";              sql << "description,is_drum,product,artists,keywords) VALUES (";
1157              sql << dirId << ",?,?,?,'GIG',?," << statBuf.st_size << ",?,?,?,?,?)";              sql << dirId << ",?,?,?,'GIG',?," << f.GetSize() << ",?,?,?,?,?)";
1158    
1159              sqlite3_stmt* pStmt = NULL;              sqlite3_stmt* pStmt = NULL;
1160    
# Line 865  namespace LinuxSampler { Line 1163  namespace LinuxSampler {
1163                  throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));                  throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1164              }              }
1165    
1166              BindTextParam(pStmt, 2, File);              String s = toEscapedFsPath(FilePath);
1167                BindTextParam(pStmt, 2, s);
1168              String ver = "";              String ver = "";
1169              if (gig->pVersion != NULL) ver = ToString(gig->pVersion->major);              if (gig->pVersion != NULL) ver = ToString(gig->pVersion->major);
1170              BindTextParam(pStmt, 4, ver);              BindTextParam(pStmt, 4, ver);
1171    
1172              if (Index == -1) {              if (Index == -1) {
1173                  int instrIndex = 0;                  int instrIndex = 0;
1174                    if (pProgress != NULL) gig->GetInstrument(0, &(pProgress->GigFileProgress)); // TODO: this workaround should be fixed
1175                  gig::Instrument* pInstrument = gig->GetFirstInstrument();                  gig::Instrument* pInstrument = gig->GetFirstInstrument();
1176                  while (pInstrument) {                  while (pInstrument) {
1177                      BindTextParam(pStmt, 7, gig->pInfo->Product);                      BindTextParam(pStmt, 7, gig->pInfo->Product);
1178                      BindTextParam(pStmt, 8, gig->pInfo->Artists);                      BindTextParam(pStmt, 8, gig->pInfo->Artists);
1179                      BindTextParam(pStmt, 9, gig->pInfo->Keywords);                      BindTextParam(pStmt, 9, gig->pInfo->Keywords);
1180                      AddGigInstrument(pStmt, DbDir, dirId, File, pInstrument, instrIndex);                      AddGigInstrument(pStmt, DbDir, dirId, FilePath, pInstrument, instrIndex);
1181    
1182                      instrIndex++;                      instrIndex++;
1183                      pInstrument = gig->GetNextInstrument();                      pInstrument = gig->GetNextInstrument();
1184                  }                  }
1185              } else {              } else {
1186                  gig::Instrument* pInstrument = gig->GetInstrument(Index);                  gig::Instrument* pInstrument;
1187                    if (pProgress == NULL) pInstrument = gig->GetInstrument(Index);
1188                    else pInstrument = gig->GetInstrument(Index, &(pProgress->GigFileProgress));
1189                  if (pInstrument != NULL) {                  if (pInstrument != NULL) {
1190                      BindTextParam(pStmt, 7, gig->pInfo->Product);                      BindTextParam(pStmt, 7, gig->pInfo->Product);
1191                      BindTextParam(pStmt, 8, gig->pInfo->Artists);                      BindTextParam(pStmt, 8, gig->pInfo->Artists);
1192                      BindTextParam(pStmt, 9, gig->pInfo->Keywords);                      BindTextParam(pStmt, 9, gig->pInfo->Keywords);
1193                      AddGigInstrument(pStmt, DbDir, dirId, File, pInstrument, Index);                      AddGigInstrument(pStmt, DbDir, dirId, FilePath, pInstrument, Index);
1194                  }                  }
1195              }              }
1196    
1197                sqlite3_finalize(pStmt);
1198              delete gig;              delete gig;
1199              delete riff;              delete riff;
1200          } catch (RIFF::Exception e) {          } catch (RIFF::Exception e) {
1201              if (gig != NULL) delete gig;              if (gig != NULL) delete gig;
1202              if (riff != NULL) delete riff;              if (riff != NULL) delete riff;
1203              std::stringstream ss;              std::stringstream ss;
1204              ss << "Failed to scan `" << File << "`: " << e.Message;              ss << "Failed to scan `" << FilePath << "`: " << e.Message;
1205                            
1206              throw Exception(ss.str());              throw Exception(ss.str());
1207          } catch (Exception e) {          } catch (Exception e) {
# Line 908  namespace LinuxSampler { Line 1211  namespace LinuxSampler {
1211          } catch (...) {          } catch (...) {
1212              if (gig != NULL) delete gig;              if (gig != NULL) delete gig;
1213              if (riff != NULL) delete riff;              if (riff != NULL) delete riff;
1214              throw Exception("Failed to scan `" + File + "`");              throw Exception("Failed to scan `" + FilePath + "`");
1215          }          }
1216      }      }
1217    
1218      void InstrumentsDb::AddGigInstrument (sqlite3_stmt* pStmt, String DbDir, int DirId, String File, gig::Instrument* pInstrument, int Index) {      void InstrumentsDb::AddGigInstrument(sqlite3_stmt* pStmt, String DbDir, int DirId, String File, gig::Instrument* pInstrument, int Index) {
1219            dmsg(2,("InstrumentsDb: AddGigInstrument(DbDir=%s,DirId=%d,File=%s,Index=%d)\n", DbDir.c_str(), DirId, File.c_str(), Index));
1220          String name = pInstrument->pInfo->Name;          String name = pInstrument->pInfo->Name;
1221          if (name == "") return;          if (name == "") return;
1222          name = GetUniqueInstrumentName(DirId, name);          name = GetUniqueInstrumentName(DirId, name);
# Line 920  namespace LinuxSampler { Line 1224  namespace LinuxSampler {
1224          std::stringstream sql2;          std::stringstream sql2;
1225          sql2 << "SELECT COUNT(*) FROM instruments WHERE instr_file=? AND ";          sql2 << "SELECT COUNT(*) FROM instruments WHERE instr_file=? AND ";
1226          sql2 << "instr_nr=" << Index;          sql2 << "instr_nr=" << Index;
1227          if (ExecSqlInt(sql2.str(), File) > 0) return;          String s = toEscapedFsPath(File);
1228            if (ExecSqlInt(sql2.str(), s) > 0) return;
1229    
1230          BindTextParam(pStmt, 1, name);          BindTextParam(pStmt, 1, name);
1231          BindIntParam(pStmt, 3, Index);          BindIntParam(pStmt, 3, Index);
# Line 949  namespace LinuxSampler { Line 1254  namespace LinuxSampler {
1254          FireInstrumentCountChanged(DbDir);          FireInstrumentCountChanged(DbDir);
1255      }      }
1256    
1257        void InstrumentsDb::DirectoryTreeWalk(String AbstractPath, DirectoryHandler* pHandler) {
1258            int DirId = GetDirectoryId(AbstractPath);
1259            if(DirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(AbstractPath));
1260            DirectoryTreeWalk(pHandler, AbstractPath, DirId, 0);
1261        }
1262    
1263        void InstrumentsDb::DirectoryTreeWalk(DirectoryHandler* pHandler, String AbstractPath, int DirId, int Level) {
1264            if(Level == 1000) throw Exception("Possible infinite loop detected");
1265            pHandler->ProcessDirectory(AbstractPath, DirId);
1266            
1267            String s;
1268            StringListPtr pDirs = GetDirectories(DirId);
1269            for(int i = 0; i < pDirs->size(); i++) {
1270                if (AbstractPath.length() == 1 && AbstractPath.at(0) == '/') {
1271                    s = "/" + pDirs->at(i);
1272                } else {
1273                    s = AbstractPath + "/" + pDirs->at(i);
1274                }
1275                DirectoryTreeWalk(pHandler, s, GetDirectoryId(DirId, pDirs->at(i)), Level + 1);
1276            }
1277        }
1278    
1279        StringListPtr InstrumentsDb::FindDirectories(String Dir, SearchQuery* pQuery, bool Recursive) {
1280            dmsg(2,("InstrumentsDb: FindDirectories(Dir=%s)\n", Dir.c_str()));
1281            DirectoryFinder directoryFinder(pQuery);
1282            
1283            BeginTransaction();
1284            try {
1285                int DirId = GetDirectoryId(Dir);
1286                if(DirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
1287    
1288                if (Recursive) DirectoryTreeWalk(Dir, &directoryFinder);
1289                else directoryFinder.ProcessDirectory(Dir, DirId);
1290            } catch (Exception e) {
1291                EndTransaction();
1292                throw e;
1293            }
1294            EndTransaction();
1295    
1296            return directoryFinder.GetDirectories();
1297        }
1298    
1299        StringListPtr InstrumentsDb::FindInstruments(String Dir, SearchQuery* pQuery, bool Recursive) {
1300            dmsg(2,("InstrumentsDb: FindInstruments(Dir=%s)\n", Dir.c_str()));
1301            InstrumentFinder instrumentFinder(pQuery);
1302            
1303            BeginTransaction();
1304            try {
1305                int DirId = GetDirectoryId(Dir);
1306                if(DirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
1307    
1308                if (Recursive) DirectoryTreeWalk(Dir, &instrumentFinder);
1309                else instrumentFinder.ProcessDirectory(Dir, DirId);
1310            } catch (Exception e) {
1311                EndTransaction();
1312                throw e;
1313            }
1314            EndTransaction();
1315    
1316            return instrumentFinder.GetInstruments();
1317        }
1318    
1319        void InstrumentsDb::BeginTransaction() {
1320            dmsg(2,("InstrumentsDb: BeginTransaction(InTransaction=%d)\n", InTransaction));
1321            DbInstrumentsMutex.Lock();
1322            if (InTransaction) return;
1323            
1324            if(db == NULL) return;
1325            sqlite3_stmt *pStmt = NULL;
1326            
1327            InTransaction = true;
1328            int res = sqlite3_prepare(db, "BEGIN TRANSACTION", -1, &pStmt, NULL);
1329            if (res != SQLITE_OK) {
1330                std::cerr << ToString(sqlite3_errmsg(db)) << std::endl;
1331                return;
1332            }
1333            
1334            res = sqlite3_step(pStmt);
1335            if(res != SQLITE_DONE) {
1336                sqlite3_finalize(pStmt);
1337                std::cerr << ToString(sqlite3_errmsg(db)) << std::endl;
1338                return;
1339            }
1340    
1341            sqlite3_finalize(pStmt);
1342        }
1343    
1344        void InstrumentsDb::EndTransaction() {
1345            dmsg(2,("InstrumentsDb: EndTransaction(InTransaction=%d)\n", InTransaction));
1346            if (!InTransaction) {
1347                DbInstrumentsMutex.Unlock();
1348                return;
1349            }
1350            InTransaction = false;
1351            
1352            if(db == NULL) {
1353                DbInstrumentsMutex.Unlock();
1354                return;
1355            }
1356            sqlite3_stmt *pStmt = NULL;
1357            
1358            int res = sqlite3_prepare(db, "END TRANSACTION", -1, &pStmt, NULL);
1359            if (res != SQLITE_OK) {
1360                std::cerr << ToString(sqlite3_errmsg(db)) << std::endl;
1361                DbInstrumentsMutex.Unlock();
1362                return;
1363            }
1364            
1365            res = sqlite3_step(pStmt);
1366            if(res != SQLITE_DONE) {
1367                sqlite3_finalize(pStmt);
1368                std::cerr << ToString(sqlite3_errmsg(db)) << std::endl;
1369                DbInstrumentsMutex.Unlock();
1370                return;
1371            }
1372    
1373            sqlite3_finalize(pStmt);
1374            DbInstrumentsMutex.Unlock();
1375        }
1376    
1377      void InstrumentsDb::ExecSql(String Sql) {      void InstrumentsDb::ExecSql(String Sql) {
1378          dmsg(2,("InstrumentsDb: ExecSql(Sql=%s)\n", Sql.c_str()));          dmsg(2,("InstrumentsDb: ExecSql(Sql=%s)\n", Sql.c_str()));
1379          sqlite3_stmt *pStmt = NULL;          sqlite3_stmt *pStmt = NULL;
# Line 1128  namespace LinuxSampler { Line 1553  namespace LinuxSampler {
1553          }          }
1554      }      }
1555    
1556        void InstrumentsDb::Regexp(sqlite3_context* pContext, int argc, sqlite3_value** ppValue) {
1557            if (argc != 2) return;
1558    
1559            String pattern = ToString(sqlite3_value_text(ppValue[0]));
1560            String str = ToString(sqlite3_value_text(ppValue[1]));
1561    
1562            if(!fnmatch(pattern.c_str(), str.c_str(), FNM_CASEFOLD)) {
1563                sqlite3_result_int(pContext, 1);
1564            }
1565        }
1566    
1567      String InstrumentsDb::GetDirectoryPath(String File) {      String InstrumentsDb::GetDirectoryPath(String File) {
1568          if (File.empty()) return String("");          if (File.empty()) return String("");
1569          if (File.at(0) != '/') String("");          if (File.at(0) != '/') String("");
# Line 1169  namespace LinuxSampler { Line 1605  namespace LinuxSampler {
1605          return Dir.substr(0, i);          return Dir.substr(0, i);
1606      }      }
1607    
1608        void InstrumentsDb::Format() {
1609            DbInstrumentsMutex.Lock();
1610            if (db != NULL) {
1611                sqlite3_close(db);
1612                db = NULL;
1613            }
1614    
1615            if (DbFile.empty()) DbFile = CONFIG_DEFAULT_INSTRUMENTS_DB_LOCATION;
1616            String bkp = DbFile + ".bkp";
1617            remove(bkp.c_str());
1618            if (rename(DbFile.c_str(), bkp.c_str()) && errno != ENOENT) {
1619                DbInstrumentsMutex.Unlock();
1620                throw Exception(String("Failed to backup database: ") + strerror(errno));
1621            }
1622            
1623            String f = DbFile;
1624            DbFile = "";
1625            try { CreateInstrumentsDb(f); }
1626            catch(Exception e) {
1627                DbInstrumentsMutex.Unlock();
1628                throw e;
1629            }
1630            DbInstrumentsMutex.Unlock();
1631            
1632            FireDirectoryCountChanged("/");
1633            FireInstrumentCountChanged("/");
1634        }
1635    
1636      void InstrumentsDb::CheckFileName(String File) {      void InstrumentsDb::CheckFileName(String File) {
1637          if (File.empty()) throw Exception("Invalid file name: " + File);          if (File.empty()) throw Exception("Invalid file name: " + File);
         if (File.find('/') != std::string::npos) {  
             throw Exception("Invalid file name: " + File);  
         }  
1638      }      }
1639    
1640      String InstrumentsDb::GetUniqueInstrumentName(int DirId, String Name) {      String InstrumentsDb::GetUniqueInstrumentName(int DirId, String Name) {
1641          dmsg(2,("InstrumentsDb: GetUniqueInstrumentName(DirId=%d,Name=%s)\n", DirId, Name.c_str()));          dmsg(2,("InstrumentsDb: GetUniqueInstrumentName(DirId=%d,Name=%s)\n", DirId, Name.c_str()));
         std::stringstream sql;  
         sql << "SELECT COUNT(*) FROM instruments WHERE dir_id=" << DirId;  
         sql << " AND instr_name=?";  
1642    
1643          if (ExecSqlInt(sql.str(), Name) == 0) return Name;          if (GetInstrumentId(DirId, Name) == -1 && GetDirectoryId(DirId, Name) == -1) return Name;
1644          std::stringstream ss;          std::stringstream ss;
1645          for(int i = 2; i < 1001; i++) {          for(int i = 2; i < 1001; i++) {
1646              ss.str("");              ss.str("");
1647              ss << Name << '[' << i << ']';              ss << Name << '[' << i << ']';
1648              if (ExecSqlInt(sql.str(), ss.str()) == 0) return ss.str();              if (GetInstrumentId(DirId, ss.str()) == -1 && GetInstrumentId(DirId, ss.str()) == -1) {
1649                    return ss.str();
1650                }
1651          }          }
1652    
1653          throw Exception("Unable to find an unique name: " + Name);          throw Exception("Unable to find an unique name: " + Name);
1654      }      }
1655    
1656        String InstrumentsDb::toDbName(String AbstractName) {
1657            for (int i = 0; i < AbstractName.length(); i++) {
1658                if (AbstractName.at(i) == '\0') AbstractName.at(i) = '/';
1659            }
1660            return AbstractName;
1661        }
1662    
1663        String InstrumentsDb::toEscapedPath(String AbstractName) {
1664            for (int i = 0; i < AbstractName.length(); i++) {
1665                if (AbstractName.at(i) == '\0')      AbstractName.replace(i++, 1, "\\x2f");
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) == '"')  AbstractName.replace(i++, 1, "\\\"");
1669                else if (AbstractName.at(i) == '\r') AbstractName.replace(i++, 1, "\\r");
1670                else if (AbstractName.at(i) == '\n') AbstractName.replace(i++, 1, "\\n");
1671            }
1672            return AbstractName;
1673        }
1674            
1675        String InstrumentsDb::toEscapedText(String text) {
1676            for (int i = 0; i < text.length(); i++) {
1677                if (text.at(i) == '\\')      text.replace(i++, 1, "\\\\");
1678                else if (text.at(i) == '\'') text.replace(i++, 1, "\\'");
1679                else if (text.at(i) == '"')  text.replace(i++, 1, "\\\"");
1680                else if (text.at(i) == '\r') text.replace(i++, 1, "\\r");
1681                else if (text.at(i) == '\n') text.replace(i++, 1, "\\n");
1682            }
1683            return text;
1684        }
1685        
1686        String InstrumentsDb::toEscapedFsPath(String FsPath) {
1687            return toEscapedText(FsPath);
1688        }
1689        
1690        String InstrumentsDb::toAbstractName(String DbName) {
1691            for (int i = 0; i < DbName.length(); i++) {
1692                if (DbName.at(i) == '/') DbName.at(i) = '\0';
1693            }
1694            return DbName;
1695        }
1696    
1697      void InstrumentsDb::FireDirectoryCountChanged(String Dir) {      void InstrumentsDb::FireDirectoryCountChanged(String Dir) {
1698          for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {          for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {
1699              llInstrumentsDbListeners.GetListener(i)->DirectoryCountChanged(Dir);              llInstrumentsDbListeners.GetListener(i)->DirectoryCountChanged(Dir);
1700          }          }
1701      }      }
1702        
1703      void InstrumentsDb::FireDirectoryInfoChanged(String Dir) {      void InstrumentsDb::FireDirectoryInfoChanged(String Dir) {
1704          for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {          for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {
1705              llInstrumentsDbListeners.GetListener(i)->DirectoryInfoChanged(Dir);              llInstrumentsDbListeners.GetListener(i)->DirectoryInfoChanged(Dir);
1706          }          }
1707      }      }
1708        
1709      void InstrumentsDb::FireDirectoryNameChanged(String Dir, String NewName) {      void InstrumentsDb::FireDirectoryNameChanged(String Dir, String NewName) {
1710          for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {          for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {
1711              llInstrumentsDbListeners.GetListener(i)->DirectoryNameChanged(Dir, NewName);              llInstrumentsDbListeners.GetListener(i)->DirectoryNameChanged(Dir, NewName);
1712          }          }
1713      }      }
1714        
1715      void InstrumentsDb::FireInstrumentCountChanged(String Dir) {      void InstrumentsDb::FireInstrumentCountChanged(String Dir) {
1716          for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {          for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {
1717              llInstrumentsDbListeners.GetListener(i)->InstrumentCountChanged(Dir);              llInstrumentsDbListeners.GetListener(i)->InstrumentCountChanged(Dir);
1718          }          }
1719      }      }
1720        
1721      void InstrumentsDb::FireInstrumentInfoChanged(String Instr) {      void InstrumentsDb::FireInstrumentInfoChanged(String Instr) {
1722          for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {          for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {
1723              llInstrumentsDbListeners.GetListener(i)->InstrumentInfoChanged(Instr);              llInstrumentsDbListeners.GetListener(i)->InstrumentInfoChanged(Instr);
1724          }          }
1725      }      }
1726        
1727      void InstrumentsDb::FireInstrumentNameChanged(String Instr, String NewName) {      void InstrumentsDb::FireInstrumentNameChanged(String Instr, String NewName) {
1728          for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {          for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {
1729              llInstrumentsDbListeners.GetListener(i)->InstrumentNameChanged(Instr, NewName);              llInstrumentsDbListeners.GetListener(i)->InstrumentNameChanged(Instr, NewName);
1730          }          }
1731      }      }
       
   
     String DirectoryScanner::DbDir;  
     String DirectoryScanner::FsDir;  
     bool DirectoryScanner::Flat;  
   
     void DirectoryScanner::Scan(String DbDir, String FsDir, bool Flat) {  
         dmsg(2,("DirectoryScanner: Scan(DbDir=%s,FsDir=%s,Flat=%d)\n", DbDir.c_str(), FsDir.c_str(), Flat));  
         if (DbDir.empty() || FsDir.empty()) throw Exception("Directory expected");  
           
         struct stat statBuf;  
         int res = stat(FsDir.c_str(), &statBuf);  
         if (res) {  
             std::stringstream ss;  
             ss << "Fail to stat `" << FsDir << "`: " << strerror(errno);  
             throw Exception(ss.str());  
         }  
1732    
1733          if (!S_ISDIR(statBuf.st_mode)) {      void InstrumentsDb::FireJobStatusChanged(int JobId) {
1734              throw Exception("Directory expected");          for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {
1735          }              llInstrumentsDbListeners.GetListener(i)->JobStatusChanged(JobId);
           
         DirectoryScanner::DbDir = DbDir;  
         DirectoryScanner::FsDir = FsDir;  
         if (DbDir.at(DbDir.length() - 1) != '/') {  
             DirectoryScanner::DbDir.append("/");  
         }  
         if (FsDir.at(FsDir.length() - 1) != '/') {  
             DirectoryScanner::FsDir.append("/");  
1736          }          }
         DirectoryScanner::Flat = Flat;  
           
         ftw(FsDir.c_str(), FtwCallback, 10);  
1737      }      }
1738    
     int DirectoryScanner::FtwCallback(const char* fpath, const struct stat* sb, int typeflag) {  
         dmsg(2,("DirectoryScanner: FtwCallback(fpath=%s)\n", fpath));  
         if (typeflag != FTW_D) return 0;  
   
         String dir = DbDir;  
         if (!Flat) {  
             String subdir = fpath;  
             if(subdir.length() > FsDir.length()) {  
                 subdir = subdir.substr(FsDir.length());  
                 dir += subdir;  
             }  
         }  
           
         InstrumentsDb* db = InstrumentsDb::GetInstrumentsDb();  
         if (!db->DirectoryExist(dir)) db->AddDirectory(dir);  
   
         db->AddInstrumentsNonrecursive(dir, String(fpath));  
   
         return 0;  
     };  
   
1739  } // namespace LinuxSampler  } // namespace LinuxSampler
   
 #endif // HAVE_SQLITE3  

Legend:
Removed from v.1161  
changed lines
  Added in v.1717

  ViewVC Help
Powered by ViewVC