/[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 1200 by iliev, Thu May 24 14:04:18 2007 UTC revision 3705 by schoenebeck, Wed Jan 8 20:26:53 2020 UTC
# Line 1  Line 1 
1  /***************************************************************************  /***************************************************************************
2   *                                                                         *   *                                                                         *
3   *   Copyright (C) 2007 Grigor Iliev                                       *   *   Copyright (C) 2007-2013 Grigor Iliev, Benno Senoner                   *
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/Path.h"
25    #include "../common/global_private.h"
26    
27  #include <iostream>  #include <iostream>
28  #include <sstream>  #include <sstream>
29  #include <vector>  #include <vector>
30  #include <dirent.h>  #include <algorithm>
31  #include <errno.h>  #include <errno.h>
32    #ifndef WIN32
33  #include <fnmatch.h>  #include <fnmatch.h>
34    #else
35    #include <direct.h>
36    #endif
37  #include "../common/Exception.h"  #include "../common/Exception.h"
38    
39  namespace LinuxSampler {  namespace LinuxSampler {
40    
41      InstrumentsDb* InstrumentsDb::pInstrumentsDb = new InstrumentsDb;      InstrumentsDb InstrumentsDb::instance;
42    
43      void InstrumentsDb::CreateInstrumentsDb(String File) {      void InstrumentsDb::CreateInstrumentsDb(String FilePath) {
44          struct stat statBuf;          if (FilePath.empty()) {
45          int res = stat(File.c_str(), &statBuf);              FilePath = GetDefaultDBLocation();
46          if (!res) {              dmsg(0,("InstrumentsDb: Creating database at default location '%s'\n", FilePath.c_str()));
47              throw Exception("File exists: " + File);          }
48    
49            File f = File(FilePath);
50            if (f.Exist()) {
51                throw Exception("File exists: " + FilePath);
52          }          }
53            
54          GetInstrumentsDb()->SetDbFile(File);          SetDbFile(FilePath);
55    
56          String sql =          String sql =
57              "  CREATE TABLE instr_dirs (                                      "              "  CREATE TABLE instr_dirs (                                      "
# Line 56  namespace LinuxSampler { Line 65  namespace LinuxSampler {
65              "      UNIQUE (parent_dir_id,dir_name)                            "              "      UNIQUE (parent_dir_id,dir_name)                            "
66              "  );                                                             ";              "  );                                                             ";
67                    
68          GetInstrumentsDb()->ExecSql(sql);          ExecSql(sql);
69    
70          sql = "INSERT INTO instr_dirs (dir_id, parent_dir_id, dir_name) VALUES (0, 0, '/');";          sql = "INSERT INTO instr_dirs (dir_id, parent_dir_id, dir_name) VALUES (0, -2, '/');";
71          GetInstrumentsDb()->ExecSql(sql);          ExecSql(sql);
72    
73          sql =          sql =
74              "  CREATE TABLE instruments (                                "              "  CREATE TABLE instruments (                                "
# Line 82  namespace LinuxSampler { Line 91  namespace LinuxSampler {
91              "      UNIQUE (dir_id,instr_name)                            "              "      UNIQUE (dir_id,instr_name)                            "
92              "  );                                                        ";              "  );                                                        ";
93                    
94          GetInstrumentsDb()->ExecSql(sql);          ExecSql(sql);
95      }      }
96    
97      InstrumentsDb::InstrumentsDb() {      InstrumentsDb::InstrumentsDb() {
98          db = NULL;          db = NULL;
         DbInstrumentsMutex = Mutex();  
99          InTransaction = false;          InTransaction = false;
100      }      }
101    
102      InstrumentsDb::~InstrumentsDb() {      InstrumentsDb::~InstrumentsDb() {
103            InstrumentsDbThread.StopThread();
104          if (db != NULL) sqlite3_close(db);          if (db != NULL) sqlite3_close(db);
105      }      }
106            
     void InstrumentsDb::Destroy() {  
         if (pInstrumentsDb != NULL) {  
             delete pInstrumentsDb;  
             pInstrumentsDb = NULL;  
         }  
     }  
   
107      void InstrumentsDb::AddInstrumentsDbListener(InstrumentsDb::Listener* l) {      void InstrumentsDb::AddInstrumentsDbListener(InstrumentsDb::Listener* l) {
108          llInstrumentsDbListeners.AddListener(l);          llInstrumentsDbListeners.AddListener(l);
109      }      }
# Line 111  namespace LinuxSampler { Line 113  namespace LinuxSampler {
113      }      }
114            
115      InstrumentsDb* InstrumentsDb::GetInstrumentsDb() {      InstrumentsDb* InstrumentsDb::GetInstrumentsDb() {
116          return pInstrumentsDb;          return &instance;
117      }      }
118            
119      void InstrumentsDb::SetDbFile(String File) {      void InstrumentsDb::SetDbFile(String File) {
120          DbInstrumentsMutex.Lock();          LockGuard lock(DbInstrumentsMutex);
121          if (File.empty() || DbFile.length() > 0) {          if (File.empty() || DbFile.length() > 0) {
             DbInstrumentsMutex.Unlock();  
122              throw Exception("Failed to set the database file");              throw Exception("Failed to set the database file");
123          }          }
124          DbFile = File;          DbFile = File;
125          DbInstrumentsMutex.Unlock();      }
126    
127        String InstrumentsDb::GetDefaultDBLocation() {
128            #ifdef WIN32
129            char* userprofile = getenv("USERPROFILE");
130            if (userprofile) {
131                String s = userprofile;
132                s += "\\.linuxsampler\\instruments.db";
133                return s;
134            } else {
135                // in case USERPROFILE is not set (which should not occur)
136                return "instruments.db";
137            }
138            #else // POSIX ...
139            String s = CONFIG_DEFAULT_INSTRUMENTS_DB_LOCATION;
140            # if defined(__APPLE__)
141            if (s.find("~") == 0)
142                s.replace(0, 1, getenv("HOME"));
143            # endif
144            return s;
145            #endif
146        }
147    
148        void InstrumentsDb::EnsureDBFileExists() {
149            if (DbFile.empty())
150                DbFile = GetDefaultDBLocation();
151                    #if defined(__APPLE__)  /* 20071224 Toshi Nagata  */
152                    if (DbFile.find("~") == 0)
153                            DbFile.replace(0, 1, getenv("HOME"));
154                    #endif
155            Path DbPath(DbFile);
156            String DbDir = DbPath.stripLastName();
157            // create directory if it does not exist yet
158            if (!DbPath.nodes().empty()) {
159                File d(DbDir);
160                if (!d.Exist()) {
161                    #ifdef WIN32
162                    if (_mkdir(DbDir.c_str()))
163                        throw Exception("Could not create instruments DB directory '" + DbDir + "'");
164                    #else
165                    if (mkdir(DbDir.c_str(), S_IRWXU))
166                        throw Exception("Could not create instruments DB directory '" + DbDir + "'");
167                    #endif
168                }
169            }
170            // create database file if it does not exist yet
171            File f(DbFile);
172            if (!f.Exist()) {
173                // formats the DB, which creates a new instruments.db file
174                Format();
175            }
176      }      }
177    
178      sqlite3* InstrumentsDb::GetDb() {      sqlite3* InstrumentsDb::GetDb() {
179          if ( db != NULL) return db;          if ( db != NULL) return db;
180    
181          if (DbFile.empty()) DbFile = "/var/lib/linuxsampler/instruments.db";          if (DbFile.empty())
182                DbFile = GetDefaultDBLocation();
183    
184            {
185                // first check if the instruments DB's directory exists, if not give up
186                Path path(DbFile);
187                String sDir = path.stripLastName();
188                File d(sDir);
189                if (!d.Exist())
190                    throw Exception("Instruments DB directory '" + sDir + "' does not exist!");
191    
192                // just to give the user a notice about the DB file being created in case it does not exist yet
193                File f(DbFile);
194                if (!f.Exist())
195                    dmsg(0,("Instruments DB file '%s' does not exist yet. Trying to create it now.\n", DbFile.c_str()));
196            }
197    
198            dmsg(0,("Opening instruments DB at '%s'\n", DbFile.c_str()));
199          int rc = sqlite3_open(DbFile.c_str(), &db);          int rc = sqlite3_open(DbFile.c_str(), &db);
200          if (rc) {          if (rc) {
201              sqlite3_close(db);              sqlite3_close(db);
202              db = NULL;              db = NULL;
203              throw Exception("Cannot open instruments database: " + DbFile);              throw Exception("Cannot open instruments database: " + DbFile);
204          }          }
205    #ifndef WIN32
206          rc = sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8, NULL, Regexp, NULL, NULL);          rc = sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8, NULL, Regexp, NULL, NULL);
207          if (rc) { throw Exception("Failed to add user function for handling regular expressions."); }          if (rc) { throw Exception("Failed to add user function for handling regular expressions."); }
208    #endif
209    
210            // TODO: remove this in the next version
211            try {
212                int i = ExecSqlInt("SELECT parent_dir_id FROM instr_dirs WHERE dir_id=0");
213                // The parent ID of the root directory should be -2 now.
214                if(i != -2) ExecSql("UPDATE instr_dirs SET parent_dir_id=-2 WHERE dir_id=0");
215            } catch(Exception e) { }
216            ////////////////////////////////////////
217                    
218          return db;          return db;
219      }      }
# Line 149  namespace LinuxSampler { Line 227  namespace LinuxSampler {
227                    
228          int count = ExecSqlInt(sql.str());          int count = ExecSqlInt(sql.str());
229    
         // 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--;  
230          return count;          return count;
231      }      }
232    
# Line 170  namespace LinuxSampler { Line 245  namespace LinuxSampler {
245              }              }
246          } catch (Exception e) {          } catch (Exception e) {
247              EndTransaction();              EndTransaction();
248              throw e;              throw; // re-throw
249          }          }
250          EndTransaction();          EndTransaction();
251          if (i == -1) throw Exception("Unkown DB directory: " + Dir);          if (i == -1) throw Exception("Unkown DB directory: " + toEscapedPath(Dir));
252                    
253          return i;          return i;
254      }      }
# Line 192  namespace LinuxSampler { Line 267  namespace LinuxSampler {
267          BeginTransaction();          BeginTransaction();
268          try {          try {
269              int dirId = GetDirectoryId(Dir);              int dirId = GetDirectoryId(Dir);
270              if(dirId == -1) throw Exception("Unknown DB directory: " + Dir);              if(dirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
271    
272              StringListPtr pDirs;              StringListPtr pDirs;
273              if (Recursive) {              if (Recursive) {
# Line 207  namespace LinuxSampler { Line 282  namespace LinuxSampler {
282              return pDirs;              return pDirs;
283          } catch (Exception e) {          } catch (Exception e) {
284              EndTransaction();              EndTransaction();
285              throw e;              throw; // re-throw
286          }          }
287      }      }
288            
# Line 215  namespace LinuxSampler { Line 290  namespace LinuxSampler {
290          std::stringstream sql;          std::stringstream sql;
291          sql << "SELECT dir_name FROM instr_dirs ";          sql << "SELECT dir_name FROM instr_dirs ";
292          sql << "WHERE parent_dir_id=" << DirId << " AND dir_id!=0";          sql << "WHERE parent_dir_id=" << DirId << " AND dir_id!=0";
293          return ExecSqlStringList(sql.str());          StringListPtr dirs = ExecSqlStringList(sql.str());
294    
295            for (int i = 0; i < dirs->size(); i++) {
296                for (int j = 0; j < dirs->at(i).length(); j++) {
297                    if (dirs->at(i).at(j) == '/') dirs->at(i).at(j) = '\0';
298                }
299            }
300    
301            return dirs;
302      }      }
303    
304      int InstrumentsDb::GetDirectoryId(String Dir) {      int InstrumentsDb::GetDirectoryId(String Dir) {
# Line 230  namespace LinuxSampler { Line 313  namespace LinuxSampler {
313          }          }
314                    
315          int id = 0, i = 1;          int id = 0, i = 1;
316          int j = Dir.find('/', i);          int j = (int) Dir.find('/', i);
317    
318          while(j != std::string::npos) {          while(j != std::string::npos) {
319              id = GetDirectoryId(id, Dir.substr(i, j - i));              id = GetDirectoryId(id, Dir.substr(i, j - i));
320              i = j + 1;              i = j + 1;
321              if (i >= Dir.length()) return id;              if (i >= Dir.length()) return id;
322              j = Dir.find('/', i);              j = (int) Dir.find('/', i);
323          }          }
324                    
325          return GetDirectoryId(id, Dir.substr(i));          return GetDirectoryId(id, Dir.substr(i));
# Line 244  namespace LinuxSampler { Line 327  namespace LinuxSampler {
327    
328      int InstrumentsDb::GetDirectoryId(int ParentDirId, String DirName) {      int InstrumentsDb::GetDirectoryId(int ParentDirId, String DirName) {
329          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()));
330            DirName = toDbName(DirName);
331          std::stringstream sql;          std::stringstream sql;
332          sql << "SELECT dir_id FROM instr_dirs WHERE parent_dir_id=";          sql << "SELECT dir_id FROM instr_dirs WHERE parent_dir_id=";
333          sql << ParentDirId << " AND dir_name=?";          sql << ParentDirId << " AND dir_name=?";
334          return ExecSqlInt(sql.str(), DirName);          return ExecSqlInt(sql.str(), DirName);
335      }      }
336    
337        int InstrumentsDb::GetDirectoryId(int InstrId) {
338            dmsg(2,("InstrumentsDb: GetDirectoryId(InstrId=%d)\n", InstrId));
339            std::stringstream sql;
340            sql << "SELECT dir_id FROM instruments WHERE instr_id=" << InstrId;
341            return ExecSqlInt(sql.str());
342        }
343    
344      String InstrumentsDb::GetDirectoryName(int DirId) {      String InstrumentsDb::GetDirectoryName(int DirId) {
345          String sql = "SELECT dir_name FROM instr_dirs WHERE dir_id=" + ToString(DirId);          String sql = "SELECT dir_name FROM instr_dirs WHERE dir_id=" + ToString(DirId);
346          String name = ExecSqlString(sql);          String name = ExecSqlString(sql);
# Line 274  namespace LinuxSampler { Line 365  namespace LinuxSampler {
365                  path = "/" + path;                  path = "/" + path;
366                  break;                  break;
367              }              }
368              path = GetDirectoryName(DirId) + path;              path = GetDirectoryName(DirId) + "/" + path;
369              DirId = GetParentDirectoryId(DirId);              DirId = GetParentDirectoryId(DirId);
370          }          }
371    
# Line 282  namespace LinuxSampler { Line 373  namespace LinuxSampler {
373    
374          return path;          return path;
375      }      }
376        
377        StringListPtr InstrumentsDb::GetInstrumentsByFile(String File) {
378            dmsg(2,("InstrumentsDb: GetInstrumentsByFile(File=%s)\n", File.c_str()));
379    
380            StringListPtr instrs(new std::vector<String>);
381            
382            BeginTransaction();
383            try {
384                File = toEscapedFsPath(File);
385                IntListPtr ids = ExecSqlIntList("SELECT instr_id FROM instruments WHERE instr_file=?", File);
386                
387                for (int i = 0; i < ids->size(); i++) {
388                    String name = GetInstrumentName(ids->at(i));
389                    String dir = GetDirectoryPath(GetDirectoryId(ids->at(i)));
390                    instrs->push_back(dir + name);
391                }
392            } catch (Exception e) {
393                EndTransaction();
394                throw; // re-throw
395            }
396            EndTransaction();
397            
398            return instrs;
399        }
400    
401      void InstrumentsDb::AddDirectory(String Dir) {      void InstrumentsDb::AddDirectory(String Dir) {
402          dmsg(2,("InstrumentsDb: AddDirectory(Dir=%s)\n", Dir.c_str()));          dmsg(2,("InstrumentsDb: AddDirectory(Dir=%s)\n", Dir.c_str()));
# Line 296  namespace LinuxSampler { Line 411  namespace LinuxSampler {
411    
412              String dirName = GetFileName(Dir);              String dirName = GetFileName(Dir);
413              if(ParentDir.empty() || dirName.empty()) {              if(ParentDir.empty() || dirName.empty()) {
414                  throw Exception("Failed to add DB directory: " + Dir);                  throw Exception("Failed to add DB directory: " + toEscapedPath(Dir));
415              }              }
416    
417              int id = GetDirectoryId(ParentDir);              int id = GetDirectoryId(ParentDir);
418              if (id == -1) throw Exception("DB directory doesn't exist: " + ParentDir);              if (id == -1) throw Exception("DB directory doesn't exist: " + toEscapedPath(ParentDir));
419              int id2 = GetDirectoryId(id, dirName);              int id2 = GetDirectoryId(id, dirName);
420              if (id2 != -1) throw Exception("DB directory already exist: " + Dir);              if (id2 != -1) throw Exception("DB directory already exist: " + toEscapedPath(Dir));
421              id2 = GetInstrumentId(id, dirName);              id2 = GetInstrumentId(id, dirName);
422              if (id2 != -1) throw Exception("Instrument with that name exist: " + Dir);              if (id2 != -1) throw Exception("Instrument with that name exist: " + toEscapedPath(Dir));
423    
424              std::stringstream sql;              std::stringstream sql;
425              sql << "INSERT INTO instr_dirs (parent_dir_id, dir_name) VALUES (";              sql << "INSERT INTO instr_dirs (parent_dir_id, dir_name) VALUES (";
426              sql << id << ", ?)";              sql << id << ", ?)";
427    
428              ExecSql(sql.str(), dirName);              ExecSql(sql.str(), toDbName(dirName));
429          } catch (Exception e) {          } catch (Exception e) {
430              EndTransaction();              EndTransaction();
431              throw e;              throw; // re-throw
432          }          }
433    
434          EndTransaction();          EndTransaction();
# Line 329  namespace LinuxSampler { Line 444  namespace LinuxSampler {
444          BeginTransaction();          BeginTransaction();
445          try {          try {
446              int dirId = GetDirectoryId(Dir);              int dirId = GetDirectoryId(Dir);
447              if (dirId == -1) throw Exception("Unknown DB directory: " + Dir);              if (dirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
448              if (dirId == 0) throw Exception("Cannot delete the root directory: " + Dir);              if (dirId == 0) throw Exception("Cannot delete the root directory: " + Dir);
449              if(ParentDir.empty()) throw Exception("Unknown parent directory");              if(ParentDir.empty()) throw Exception("Unknown parent directory");
450              if (Force) RemoveDirectoryContent(dirId);              if (Force) RemoveDirectoryContent(dirId);
451              RemoveDirectory(dirId);              RemoveDirectory(dirId);
452          } catch (Exception e) {          } catch (Exception e) {
453              EndTransaction();              EndTransaction();
454              throw e;              throw; // re-throw
455          }          }
456    
457          EndTransaction();          EndTransaction();
# Line 395  namespace LinuxSampler { Line 510  namespace LinuxSampler {
510    
511      bool InstrumentsDb::DirectoryExist(String Dir) {      bool InstrumentsDb::DirectoryExist(String Dir) {
512          dmsg(2,("InstrumentsDb: DirectoryExist(Dir=%s)\n", Dir.c_str()));          dmsg(2,("InstrumentsDb: DirectoryExist(Dir=%s)\n", Dir.c_str()));
513          bool b;          {
514                LockGuard lock(DbInstrumentsMutex);
515          DbInstrumentsMutex.Lock();              return GetDirectoryId(Dir) != -1;
         try { b = GetDirectoryId(Dir) != -1; }  
         catch (Exception e) {  
             DbInstrumentsMutex.Unlock();  
             throw e;  
516          }          }
         DbInstrumentsMutex.Unlock();  
   
         return b;  
517      }      }
518    
519      DbDirectory InstrumentsDb::GetDirectoryInfo(String Dir) {      DbDirectory InstrumentsDb::GetDirectoryInfo(String Dir) {
# Line 416  namespace LinuxSampler { Line 524  namespace LinuxSampler {
524    
525          try {          try {
526              int id = GetDirectoryId(Dir);              int id = GetDirectoryId(Dir);
527              if(id == -1) throw Exception("Unknown DB directory: " + Dir);              if(id == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
528    
529              sqlite3_stmt *pStmt = NULL;              sqlite3_stmt *pStmt = NULL;
530              std::stringstream sql;              std::stringstream sql;
# Line 439  namespace LinuxSampler { Line 547  namespace LinuxSampler {
547                  if (res != SQLITE_DONE) {                  if (res != SQLITE_DONE) {
548                      throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));                      throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
549                  } else {                  } else {
550                      throw Exception("Unknown DB directory: " + Dir);                      throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
551                  }                  }
552              }              }
553                            
554              sqlite3_finalize(pStmt);              sqlite3_finalize(pStmt);
555          } catch (Exception e) {          } catch (Exception e) {
556              EndTransaction();              EndTransaction();
557              throw e;              throw; // re-throw
558          }          }
559    
560          EndTransaction();          EndTransaction();
# Line 456  namespace LinuxSampler { Line 564  namespace LinuxSampler {
564      void InstrumentsDb::RenameDirectory(String Dir, String Name) {      void InstrumentsDb::RenameDirectory(String Dir, String Name) {
565          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()));
566          CheckFileName(Name);          CheckFileName(Name);
567            String dbName = toDbName(Name);
568    
569          BeginTransaction();          BeginTransaction();
570          try {          try {
571              int dirId = GetDirectoryId(Dir);              int dirId = GetDirectoryId(Dir);
572              if (dirId == -1) throw Exception("Unknown DB directory: " + Dir);              if (dirId == -1) throw Exception("Unknown DB directory: " + toEscapedText(Dir));
573    
574              std::stringstream sql;              std::stringstream sql;
575              sql << "SELECT parent_dir_id FROM instr_dirs WHERE dir_id=" <<  dirId;              sql << "SELECT parent_dir_id FROM instr_dirs WHERE dir_id=" <<  dirId;
576    
577              int parent = ExecSqlInt(sql.str());              int parent = ExecSqlInt(sql.str());
578              if (parent == -1) throw Exception("Unknown parent directory: " + Dir);              if (parent == -1) throw Exception("Unknown parent directory: " + toEscapedPath(Dir));
579              if (GetDirectoryId(parent, Name) != -1) {  
580                  throw Exception("Cannot rename. Directory with that name already exists: " + Name);              if (GetDirectoryId(parent, dbName) != -1) {
581                    String s = toEscapedPath(Name);
582                    throw Exception("Cannot rename. Directory with that name already exists: " + s);
583              }              }
584    
585              if (GetInstrumentId(parent, Name) != -1) {              if (GetInstrumentId(parent, dbName) != -1) {
586                  throw Exception("Cannot rename. Instrument with that name exist: " + Dir);                  throw Exception("Cannot rename. Instrument with that name exist: " + toEscapedPath(Dir));
587              }              }
588    
589              sql.str("");              sql.str("");
590              sql << "UPDATE instr_dirs SET dir_name=? WHERE dir_id=" << dirId;              sql << "UPDATE instr_dirs SET dir_name=? WHERE dir_id=" << dirId;
591              ExecSql(sql.str(), Name);              ExecSql(sql.str(), dbName);
592          } catch (Exception e) {          } catch (Exception e) {
593              EndTransaction();              EndTransaction();
594              throw e;              throw; // re-throw
595          }          }
596    
597          EndTransaction();          EndTransaction();
598          FireDirectoryNameChanged(Dir, Name);          FireDirectoryNameChanged(Dir, toAbstractName(Name));
599      }      }
600    
601      void InstrumentsDb::MoveDirectory(String Dir, String Dst) {      void InstrumentsDb::MoveDirectory(String Dir, String Dst) {
# Line 497  namespace LinuxSampler { Line 608  namespace LinuxSampler {
608          BeginTransaction();          BeginTransaction();
609          try {          try {
610              int dirId = GetDirectoryId(Dir);              int dirId = GetDirectoryId(Dir);
611              if (dirId == -1) throw Exception("Unknown DB directory: " + Dir);              if (dirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
612              int dstId = GetDirectoryId(Dst);              int dstId = GetDirectoryId(Dst);
613              if (dstId == -1) throw Exception("Unknown DB directory: " + Dst);              if (dstId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dst));
614              if (dirId == dstId) {              if (dirId == dstId) {
615                  throw Exception("Cannot move directory to itself");                  throw Exception("Cannot move directory to itself");
616              }              }
# Line 515  namespace LinuxSampler { Line 626  namespace LinuxSampler {
626              String dirName = GetFileName(Dir);              String dirName = GetFileName(Dir);
627    
628              int id2 = GetDirectoryId(dstId, dirName);              int id2 = GetDirectoryId(dstId, dirName);
629              if (id2 != -1) throw Exception("DB directory already exist: " + dirName);              if (id2 != -1) throw Exception("DB directory already exist: " + toEscapedPath(dirName));
630              id2 = GetInstrumentId(dstId, dirName);              id2 = GetInstrumentId(dstId, dirName);
631              if (id2 != -1) throw Exception("Instrument with that name exist: " + dirName);              if (id2 != -1) throw Exception("Instrument with that name exist: " + toEscapedPath(dirName));
632    
633              std::stringstream sql;              std::stringstream sql;
634              sql << "UPDATE instr_dirs SET parent_dir_id=" << dstId;              sql << "UPDATE instr_dirs SET parent_dir_id=" << dstId;
# Line 525  namespace LinuxSampler { Line 636  namespace LinuxSampler {
636              ExecSql(sql.str());              ExecSql(sql.str());
637          } catch (Exception e) {          } catch (Exception e) {
638              EndTransaction();              EndTransaction();
639              throw e;              throw; // re-throw
640          }          }
641    
642          EndTransaction();          EndTransaction();
# Line 543  namespace LinuxSampler { Line 654  namespace LinuxSampler {
654          BeginTransaction();          BeginTransaction();
655          try {          try {
656              int dirId = GetDirectoryId(Dir);              int dirId = GetDirectoryId(Dir);
657              if (dirId == -1) throw Exception("Unknown DB directory: " + Dir);              if (dirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
658              int dstId = GetDirectoryId(Dst);              int dstId = GetDirectoryId(Dst);
659              if (dstId == -1) throw Exception("Unknown DB directory: " + Dst);              if (dstId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dst));
660              if (dirId == dstId) {              if (dirId == dstId) {
661                  throw Exception("Cannot copy directory to itself");                  throw Exception("Cannot copy directory to itself");
662              }              }
# Line 561  namespace LinuxSampler { Line 672  namespace LinuxSampler {
672              String dirName = GetFileName(Dir);              String dirName = GetFileName(Dir);
673    
674              int id2 = GetDirectoryId(dstId, dirName);              int id2 = GetDirectoryId(dstId, dirName);
675              if (id2 != -1) throw Exception("DB directory already exist: " + dirName);              if (id2 != -1) throw Exception("DB directory already exist: " + toEscapedPath(dirName));
676              id2 = GetInstrumentId(dstId, dirName);              id2 = GetInstrumentId(dstId, dirName);
677              if (id2 != -1) throw Exception("Instrument with that name exist: " + dirName);              if (id2 != -1) throw Exception("Instrument with that name exist: " + toEscapedPath(dirName));
678    
679              DirectoryCopier directoryCopier(ParentDir, Dst);              DirectoryCopier directoryCopier(ParentDir, Dst);
680              DirectoryTreeWalk(Dir, &directoryCopier);              DirectoryTreeWalk(Dir, &directoryCopier);
681          } catch (Exception e) {          } catch (Exception e) {
682              EndTransaction();              EndTransaction();
683              throw e;              throw; // re-throw
684          }          }
685    
686          EndTransaction();          EndTransaction();
# Line 581  namespace LinuxSampler { Line 692  namespace LinuxSampler {
692          BeginTransaction();          BeginTransaction();
693          try {          try {
694              int id = GetDirectoryId(Dir);              int id = GetDirectoryId(Dir);
695              if(id == -1) throw Exception("Unknown DB directory: " + Dir);              if(id == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
696    
697              std::stringstream sql;              std::stringstream sql;
698              sql << "UPDATE instr_dirs SET description=?,modified=CURRENT_TIMESTAMP ";              sql << "UPDATE instr_dirs SET description=?,modified=CURRENT_TIMESTAMP ";
# Line 590  namespace LinuxSampler { Line 701  namespace LinuxSampler {
701              ExecSql(sql.str(), Desc);              ExecSql(sql.str(), Desc);
702          } catch (Exception e) {          } catch (Exception e) {
703              EndTransaction();              EndTransaction();
704              throw e;              throw; // re-throw
705          }          }
706          EndTransaction();          EndTransaction();
707                    
708          FireDirectoryInfoChanged(Dir);          FireDirectoryInfoChanged(Dir);
709      }      }
710    
711      int InstrumentsDb::AddInstruments(ScanMode Mode, String DbDir, String FsDir, bool bBackground) {      int InstrumentsDb::AddInstruments(ScanMode Mode, String DbDir, String FsDir, bool bBackground, bool insDir) {
712          dmsg(2,("InstrumentsDb: AddInstruments(Mode=%d,DbDir=%s,FsDir=%s,bBackground=%d)\n", Mode, DbDir.c_str(), FsDir.c_str(), bBackground));          dmsg(2,("InstrumentsDb: AddInstruments(Mode=%d,DbDir=%s,FsDir=%s,bBackground=%d,insDir=%d)\n", Mode, DbDir.c_str(), FsDir.c_str(), bBackground, insDir));
713          if(!bBackground) {          if(!bBackground) {
714              switch (Mode) {              switch (Mode) {
715                  case NON_RECURSIVE:                  case NON_RECURSIVE:
716                      AddInstrumentsNonrecursive(DbDir, FsDir);                      AddInstrumentsNonrecursive(DbDir, FsDir, insDir);
717                      break;                      break;
718                  case RECURSIVE:                  case RECURSIVE:
719                      AddInstrumentsRecursive(DbDir, FsDir);                      AddInstrumentsRecursive(DbDir, FsDir, false, insDir);
720                      break;                      break;
721                  case FLAT:                  case FLAT:
722                      AddInstrumentsRecursive(DbDir, FsDir, true);                      AddInstrumentsRecursive(DbDir, FsDir, true, insDir);
723                      break;                      break;
724                  default:                  default:
725                      throw Exception("Unknown scan mode");                      throw Exception("Unknown scan mode");
# Line 619  namespace LinuxSampler { Line 730  namespace LinuxSampler {
730    
731          ScanJob job;          ScanJob job;
732          int jobId = Jobs.AddJob(job);          int jobId = Jobs.AddJob(job);
733          InstrumentsDbThread.Execute(new AddInstrumentsJob(jobId, Mode, DbDir, FsDir));          InstrumentsDbThread.Execute(new AddInstrumentsJob(jobId, Mode, DbDir, FsDir, insDir));
734    
735          return jobId;          return jobId;
736      }      }
# Line 627  namespace LinuxSampler { Line 738  namespace LinuxSampler {
738      int InstrumentsDb::AddInstruments(String DbDir, String FilePath, int Index, bool bBackground) {      int InstrumentsDb::AddInstruments(String DbDir, String FilePath, int Index, bool bBackground) {
739          dmsg(2,("InstrumentsDb: AddInstruments(DbDir=%s,FilePath=%s,Index=%d,bBackground=%d)\n", DbDir.c_str(), FilePath.c_str(), Index, bBackground));          dmsg(2,("InstrumentsDb: AddInstruments(DbDir=%s,FilePath=%s,Index=%d,bBackground=%d)\n", DbDir.c_str(), FilePath.c_str(), Index, bBackground));
740          if(!bBackground) {          if(!bBackground) {
741              AddInstruments(DbDir, FilePath, Index);              AddInstruments(DbDir, false, FilePath, Index);
742              return -1;              return -1;
743          }          }
744    
745          ScanJob job;          ScanJob job;
746          int jobId = Jobs.AddJob(job);          int jobId = Jobs.AddJob(job);
747          InstrumentsDbThread.Execute(new AddInstrumentsFromFileJob(jobId, DbDir, FilePath, Index));          InstrumentsDbThread.Execute(new AddInstrumentsFromFileJob(jobId, DbDir, FilePath, Index, false));
748    
749          return jobId;          return jobId;
750      }      }
751    
752      void InstrumentsDb::AddInstruments(String DbDir, String FilePath, int Index, ScanProgress* pProgress) {      void InstrumentsDb::AddInstruments(String DbDir, bool insDir, String FilePath, int Index, ScanProgress* pProgress) {
753          dmsg(2,("InstrumentsDb: AddInstruments(DbDir=%s,FilePath=%s,Index=%d)\n", DbDir.c_str(), FilePath.c_str(), Index));          dmsg(2,("InstrumentsDb: AddInstruments(DbDir=%s,insDir=%d,FilePath=%s,Index=%d)\n", DbDir.c_str(), insDir, FilePath.c_str(), Index));
754          if (DbDir.empty() || FilePath.empty()) return;          if (DbDir.empty() || FilePath.empty()) return;
755                    
756          DbInstrumentsMutex.Lock();          {
757          try {              LockGuard lock(DbInstrumentsMutex);
758    
759              int dirId = GetDirectoryId(DbDir);              int dirId = GetDirectoryId(DbDir);
760              if (dirId == -1) throw Exception("Invalid DB directory: " + DbDir);              if (dirId == -1) throw Exception("Invalid DB directory: " + toEscapedText(DbDir));
761    
762              struct stat statBuf;              File f = File(FilePath);
763              int res = stat(FilePath.c_str(), &statBuf);              if (!f.Exist()) {
             if (res) {  
764                  std::stringstream ss;                  std::stringstream ss;
765                  ss << "Fail to stat `" << FilePath << "`: " << strerror(errno);                  ss << "Fail to stat `" << FilePath << "`: " << f.GetErrorMsg();
766                  throw Exception(ss.str());                  throw Exception(ss.str());
767              }              }
768    
769              if (!S_ISREG(statBuf.st_mode)) {              if (!f.IsFile()) {
770                  std::stringstream ss;                  std::stringstream ss;
771                  ss << "`" << FilePath << "` is not an instrument file";                  ss << "`" << FilePath << "` is not an instrument file";
772                  throw Exception(ss.str());                  throw Exception(ss.str());
773              }              }
774    
775              AddInstrumentsFromFile(DbDir, FilePath, Index, pProgress);              String dir = insDir ? PrepareSubdirectory(DbDir, FilePath) : DbDir;
776          } catch (Exception e) {              AddInstrumentsFromFile(dir, FilePath, Index, pProgress);
             DbInstrumentsMutex.Unlock();  
             throw e;  
777          }          }
   
         DbInstrumentsMutex.Unlock();  
778      }      }
779    
780      void InstrumentsDb::AddInstrumentsNonrecursive(String DbDir, String FsDir, ScanProgress* pProgress) {      void InstrumentsDb::AddInstrumentsNonrecursive(String DbDir, String FsDir, bool insDir, ScanProgress* pProgress) {
781          dmsg(2,("InstrumentsDb: AddInstrumentsNonrecursive(DbDir=%s,FsDir=%s)\n", DbDir.c_str(), FsDir.c_str()));          dmsg(2,("InstrumentsDb: AddInstrumentsNonrecursive(DbDir=%s,FsDir=%s,insDir=%d)\n", DbDir.c_str(), FsDir.c_str(), insDir));
782          if (DbDir.empty() || FsDir.empty()) return;          if (DbDir.empty() || FsDir.empty()) return;
783                    
784          DbInstrumentsMutex.Lock();          {
785          try {              LockGuard lock(DbInstrumentsMutex);
786    
787              int dirId = GetDirectoryId(DbDir);              int dirId = GetDirectoryId(DbDir);
788              if (dirId == -1) throw Exception("Invalid DB directory: " + DbDir);              if (dirId == -1) throw Exception("Invalid DB directory: " + toEscapedPath(DbDir));
789    
790              struct stat statBuf;              File f = File(FsDir);
791              int res = stat(FsDir.c_str(), &statBuf);              if (!f.Exist()) {
             if (res) {  
792                  std::stringstream ss;                  std::stringstream ss;
793                  ss << "Fail to stat `" << FsDir << "`: " << strerror(errno);                  ss << "Fail to stat `" << FsDir << "`: " << f.GetErrorMsg();
794                  throw Exception(ss.str());                  throw Exception(ss.str());
795              }              }
796    
797              if (!S_ISDIR(statBuf.st_mode)) {              if (!f.IsDirectory()) {
798                  throw Exception("Directory expected");                  throw Exception("Directory expected: " + FsDir);
799              }              }
800                            
801              if (FsDir.at(FsDir.length() - 1) != '/') FsDir.append("/");              if (FsDir.at(FsDir.length() - 1) != File::DirSeparator) {
802                    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::cerr << ss.str();  
                 DbInstrumentsMutex.Unlock();  
                 return;  
803              }              }
804                
805              struct dirent* pEnt = readdir(pDir);              try {
806              while (pEnt != NULL) {                  FileListPtr fileList = File::GetFiles(FsDir);
807                  if (pEnt->d_type != DT_REG) {                  for (int i = 0; i < fileList->size(); i++) {
808                      pEnt = readdir(pDir);                      String dir = insDir ? PrepareSubdirectory(DbDir, fileList->at(i)) : DbDir;
809                      continue;                                          AddInstrumentsFromFile(dir, FsDir + fileList->at(i), -1, pProgress);
810                  }                  }
811                } catch(Exception e) {
812                  AddInstrumentsFromFile(DbDir, FsDir + String(pEnt->d_name), -1, pProgress);                  e.PrintMessage();
                 pEnt = readdir(pDir);  
             }  
   
             if (closedir(pDir)) {  
                 std::stringstream ss;  
                 ss << "Failed to close directory `" << FsDir << "`: ";  
                 ss << strerror(errno);  
                 std::cerr << ss.str();  
813              }              }
         } catch (Exception e) {  
             DbInstrumentsMutex.Unlock();  
             throw e;  
814          }          }
   
         DbInstrumentsMutex.Unlock();  
815      }      }
816    
817      void InstrumentsDb::AddInstrumentsRecursive(String DbDir, String FsDir, bool Flat, ScanProgress* pProgress) {      void InstrumentsDb::AddInstrumentsRecursive(String DbDir, String FsDir, bool Flat, bool insDir, ScanProgress* pProgress) {
818          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,insDir=%d)\n", DbDir.c_str(), FsDir.c_str(), Flat, insDir));
819          if (pProgress != NULL) {          if (pProgress != NULL) {
820              pProgress->SetTotalFileCount(InstrumentFileCounter::Count(FsDir));              InstrumentFileCounter c;
821                pProgress->SetTotalFileCount(c.Count(FsDir));
822          }          }
823    
824          DirectoryScanner::Scan(DbDir, FsDir, Flat, pProgress);          DirectoryScanner d;
825            d.Scan(DbDir, FsDir, Flat, insDir, pProgress);
826      }      }
827    
828      int InstrumentsDb::GetInstrumentCount(int DirId) {      int InstrumentsDb::GetInstrumentCount(int DirId) {
# Line 762  namespace LinuxSampler { Line 850  namespace LinuxSampler {
850              }              }
851          } catch (Exception e) {          } catch (Exception e) {
852              EndTransaction();              EndTransaction();
853              throw e;              throw; // re-throw
854          }          }
855          EndTransaction();          EndTransaction();
856    
857          if (i == -1) throw Exception("Unknown Db directory: " + Dir);          if (i == -1) throw Exception("Unknown Db directory: " + toEscapedPath(Dir));
858          return i;          return i;
859      }      }
860    
# Line 782  namespace LinuxSampler { Line 870  namespace LinuxSampler {
870          BeginTransaction();          BeginTransaction();
871          try {          try {
872              int dirId = GetDirectoryId(Dir);              int dirId = GetDirectoryId(Dir);
873              if(dirId == -1) throw Exception("Unknown DB directory: " + Dir);              if(dirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
874    
875              StringListPtr pInstrs;              StringListPtr pInstrs;
876    
# Line 796  namespace LinuxSampler { Line 884  namespace LinuxSampler {
884                  sql << "SELECT instr_name FROM instruments WHERE dir_id=" << dirId;                  sql << "SELECT instr_name FROM instruments WHERE dir_id=" << dirId;
885    
886                  pInstrs = ExecSqlStringList(sql.str());                  pInstrs = ExecSqlStringList(sql.str());
887                    // Converting to abstract names
888                    for (int i = 0; i < pInstrs->size(); i++) {
889                        for (int j = 0; j < pInstrs->at(i).length(); j++) {
890                            if (pInstrs->at(i).at(j) == '/') pInstrs->at(i).at(j) = '\0';
891                        }
892                    }
893              }              }
894              EndTransaction();              EndTransaction();
895              return pInstrs;              return pInstrs;
896          } catch (Exception e) {          } catch (Exception e) {
897              EndTransaction();              EndTransaction();
898              throw e;              throw; // re-throw
899          }          }
900      }      }
901    
# Line 820  namespace LinuxSampler { Line 914  namespace LinuxSampler {
914          std::stringstream sql;          std::stringstream sql;
915          sql << "SELECT instr_id FROM instruments WHERE dir_id=";          sql << "SELECT instr_id FROM instruments WHERE dir_id=";
916          sql << DirId << " AND instr_name=?";          sql << DirId << " AND instr_name=?";
917          return ExecSqlInt(sql.str(), InstrName);          return ExecSqlInt(sql.str(), toDbName(InstrName));
918      }      }
919    
920      String InstrumentsDb::GetInstrumentName(int InstrId) {      String InstrumentsDb::GetInstrumentName(int InstrId) {
921          dmsg(2,("InstrumentsDb: GetInstrumentName(InstrId=%d)\n", InstrId));          dmsg(2,("InstrumentsDb: GetInstrumentName(InstrId=%d)\n", InstrId));
922          std::stringstream sql;          std::stringstream sql;
923          sql << "SELECT instr_name FROM instruments WHERE instr_id=" << InstrId;          sql << "SELECT instr_name FROM instruments WHERE instr_id=" << InstrId;
924          return ExecSqlString(sql.str());          return toAbstractName(ExecSqlString(sql.str()));
925      }      }
926            
927      void InstrumentsDb::RemoveInstrument(String Instr) {      void InstrumentsDb::RemoveInstrument(String Instr) {
# Line 839  namespace LinuxSampler { Line 933  namespace LinuxSampler {
933          try {          try {
934              int instrId = GetInstrumentId(Instr);              int instrId = GetInstrumentId(Instr);
935              if(instrId == -1) {              if(instrId == -1) {
936                  throw Exception("The specified instrument does not exist: " + Instr);                  throw Exception("The specified instrument does not exist: " + toEscapedPath(Instr));
937              }              }
938              RemoveInstrument(instrId);              RemoveInstrument(instrId);
939          } catch (Exception e) {          } catch (Exception e) {
940              EndTransaction();              EndTransaction();
941              throw e;              throw; // re-throw
942          }          }
943          EndTransaction();          EndTransaction();
944          FireInstrumentCountChanged(ParentDir);          FireInstrumentCountChanged(ParentDir);
# Line 874  namespace LinuxSampler { Line 968  namespace LinuxSampler {
968          BeginTransaction();          BeginTransaction();
969          try {          try {
970              int id = GetInstrumentId(Instr);              int id = GetInstrumentId(Instr);
971              if(id == -1) throw Exception("Unknown DB instrument: " + Instr);              if(id == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
972              i = GetInstrumentInfo(id);              i = GetInstrumentInfo(id);
973          } catch (Exception e) {          } catch (Exception e) {
974              EndTransaction();              EndTransaction();
975              throw e;              throw; // re-throw
976          }          }
977          EndTransaction();          EndTransaction();
978                    
# Line 933  namespace LinuxSampler { Line 1027  namespace LinuxSampler {
1027          BeginTransaction();          BeginTransaction();
1028          try {          try {
1029              int dirId = GetDirectoryId(GetDirectoryPath(Instr));              int dirId = GetDirectoryId(GetDirectoryPath(Instr));
1030              if (dirId == -1) throw Exception("Unknown DB instrument: " + Instr);              if (dirId == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
1031    
1032              int instrId = GetInstrumentId(dirId, GetFileName(Instr));              int instrId = GetInstrumentId(dirId, GetFileName(Instr));
1033              if (instrId == -1) throw Exception("Unknown DB instrument: " + Instr);              if (instrId == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
1034    
1035              if (GetInstrumentId(dirId, Name) != -1) {              if (GetInstrumentId(dirId, Name) != -1) {
1036                  throw Exception("Cannot rename. Instrument with that name already exists: " + Name);                  String s = toEscapedPath(Name);
1037                    throw Exception("Cannot rename. Instrument with that name already exists: " + s);
1038              }              }
1039    
1040              if (GetDirectoryId(dirId, Name) != -1) {              if (GetDirectoryId(dirId, Name) != -1) {
1041                  throw Exception("Cannot rename. Directory with that name already exists: " + Name);                  String s = toEscapedPath(Name);
1042                    throw Exception("Cannot rename. Directory with that name already exists: " + s);
1043              }              }
1044    
1045              std::stringstream sql;              std::stringstream sql;
1046              sql << "UPDATE instruments SET instr_name=? WHERE instr_id=" << instrId;              sql << "UPDATE instruments SET instr_name=? WHERE instr_id=" << instrId;
1047              ExecSql(sql.str(), Name);              ExecSql(sql.str(), toDbName(Name));
1048          } catch (Exception e) {          } catch (Exception e) {
1049              EndTransaction();              EndTransaction();
1050              throw e;              throw; // re-throw
1051          }          }
1052          EndTransaction();          EndTransaction();
1053          FireInstrumentNameChanged(Instr, Name);          FireInstrumentNameChanged(Instr, toAbstractName(Name));
1054      }      }
1055    
1056      void InstrumentsDb::MoveInstrument(String Instr, String Dst) {      void InstrumentsDb::MoveInstrument(String Instr, String Dst) {
# Line 964  namespace LinuxSampler { Line 1060  namespace LinuxSampler {
1060    
1061          BeginTransaction();          BeginTransaction();
1062          try {          try {
1063              int dirId = GetDirectoryId(GetDirectoryPath(Instr));              int dirId = GetDirectoryId(ParentDir);
1064              if (dirId == -1) throw Exception("Unknown DB instrument: " + Instr);              if (dirId == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
1065    
1066              String instrName = GetFileName(Instr);              String instrName = GetFileName(Instr);
1067              int instrId = GetInstrumentId(dirId, instrName);              int instrId = GetInstrumentId(dirId, instrName);
1068              if (instrId == -1) throw Exception("Unknown DB instrument: " + Instr);              if (instrId == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
1069    
1070              int dstId = GetDirectoryId(Dst);              int dstId = GetDirectoryId(Dst);
1071              if (dstId == -1) throw Exception("Unknown DB directory: " + Dst);              if (dstId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dst));
1072              if (dirId == dstId) {              if (dirId == dstId) {
1073                  EndTransaction();                  EndTransaction();
1074                  return;                  return;
1075              }              }
1076    
1077              if (GetInstrumentId(dstId, instrName) != -1) {              if (GetInstrumentId(dstId, instrName) != -1) {
1078                  throw Exception("Cannot move. Instrument with that name already exists: " + instrName);                  String s = toEscapedPath(instrName);
1079                    throw Exception("Cannot move. Instrument with that name already exists: " + s);
1080              }              }
1081    
1082              if (GetDirectoryId(dstId, instrName) != -1) {              if (GetDirectoryId(dstId, instrName) != -1) {
1083                  throw Exception("Cannot move. Directory with that name already exists: " + instrName);                  String s = toEscapedPath(instrName);
1084                    throw Exception("Cannot move. Directory with that name already exists: " + s);
1085              }              }
1086    
1087              std::stringstream sql;              std::stringstream sql;
# Line 992  namespace LinuxSampler { Line 1090  namespace LinuxSampler {
1090              ExecSql(sql.str());              ExecSql(sql.str());
1091          } catch (Exception e) {          } catch (Exception e) {
1092              EndTransaction();              EndTransaction();
1093              throw e;              throw; // re-throw
1094          }          }
1095          EndTransaction();          EndTransaction();
1096          FireInstrumentCountChanged(ParentDir);          FireInstrumentCountChanged(ParentDir);
# Line 1007  namespace LinuxSampler { Line 1105  namespace LinuxSampler {
1105          BeginTransaction();          BeginTransaction();
1106          try {          try {
1107              int dirId = GetDirectoryId(GetDirectoryPath(Instr));              int dirId = GetDirectoryId(GetDirectoryPath(Instr));
1108              if (dirId == -1) throw Exception("Unknown DB instrument: " + Instr);              if (dirId == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
1109    
1110              String instrName = GetFileName(Instr);              String instrName = GetFileName(Instr);
1111              int instrId = GetInstrumentId(dirId, instrName);              int instrId = GetInstrumentId(dirId, instrName);
1112              if (instrId == -1) throw Exception("Unknown DB instrument: " + Instr);              if (instrId == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
1113    
1114              int dstId = GetDirectoryId(Dst);              int dstId = GetDirectoryId(Dst);
1115              if (dstId == -1) throw Exception("Unknown DB directory: " + Dst);              if (dstId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dst));
1116              if (dirId == dstId) {              if (dirId == dstId) {
1117                  EndTransaction();                  EndTransaction();
1118                  return;                  return;
1119              }              }
1120    
             if (GetInstrumentId(dstId, instrName) != -1) {  
                 throw Exception("Cannot copy. Instrument with that name already exists: " + instrName);  
             }  
   
             if (GetDirectoryId(dstId, instrName) != -1) {  
                 throw Exception("Cannot copy. Directory with that name already exists: " + instrName);  
             }  
   
1121              CopyInstrument(instrId, instrName, dstId, Dst);              CopyInstrument(instrId, instrName, dstId, Dst);
1122          } catch (Exception e) {          } catch (Exception e) {
1123              EndTransaction();              EndTransaction();
1124              throw e;              throw; // re-throw
1125          }          }
1126          EndTransaction();          EndTransaction();
1127                    
1128      }      }
1129    
1130      void InstrumentsDb::CopyInstrument(int InstrId, String InstrName, int DstDirId, String DstDir) {      void InstrumentsDb::CopyInstrument(int InstrId, String InstrName, int DstDirId, String DstDir) {
1131            if (GetInstrumentId(DstDirId, InstrName) != -1) {
1132                String s = toEscapedPath(InstrName);
1133                throw Exception("Cannot copy. Instrument with that name already exists: " + s);
1134            }
1135    
1136            if (GetDirectoryId(DstDirId, InstrName) != -1) {
1137                String s = toEscapedPath(InstrName);
1138                throw Exception("Cannot copy. Directory with that name already exists: " + s);
1139            }
1140    
1141          DbInstrument i = GetInstrumentInfo(InstrId);          DbInstrument i = GetInstrumentInfo(InstrId);
1142          sqlite3_stmt *pStmt = NULL;          sqlite3_stmt *pStmt = NULL;
1143          std::stringstream sql;          std::stringstream sql;
# Line 1051  namespace LinuxSampler { Line 1151  namespace LinuxSampler {
1151              throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));              throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1152          }          }
1153    
1154          BindTextParam(pStmt, 1, InstrName);          String s = toDbName(InstrName);
1155            BindTextParam(pStmt, 1, s);
1156          BindTextParam(pStmt, 2, i.InstrFile);          BindTextParam(pStmt, 2, i.InstrFile);
1157          BindTextParam(pStmt, 3, i.FormatFamily);          BindTextParam(pStmt, 3, i.FormatFamily);
1158          BindTextParam(pStmt, 4, i.FormatVersion);          BindTextParam(pStmt, 4, i.FormatVersion);
# Line 1076  namespace LinuxSampler { Line 1177  namespace LinuxSampler {
1177          BeginTransaction();          BeginTransaction();
1178          try {          try {
1179              int id = GetInstrumentId(Instr);              int id = GetInstrumentId(Instr);
1180              if(id == -1) throw Exception("Unknown DB instrument: " + Instr);              if(id == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
1181    
1182              std::stringstream sql;              std::stringstream sql;
1183              sql << "UPDATE instruments SET description=?,modified=CURRENT_TIMESTAMP ";              sql << "UPDATE instruments SET description=?,modified=CURRENT_TIMESTAMP ";
# Line 1085  namespace LinuxSampler { Line 1186  namespace LinuxSampler {
1186              ExecSql(sql.str(), Desc);              ExecSql(sql.str(), Desc);
1187          } catch (Exception e) {          } catch (Exception e) {
1188              EndTransaction();              EndTransaction();
1189              throw e;              throw; // re-throw
1190          }          }
1191          EndTransaction();          EndTransaction();
1192          FireInstrumentInfoChanged(Instr);          FireInstrumentInfoChanged(Instr);
1193      }      }
1194    
1195      void InstrumentsDb::AddInstrumentsFromFile(String DbDir, String File, int Index, ScanProgress* pProgress) {      void InstrumentsDb::AddInstrumentsFromFile(String DbDir, String file, int Index, ScanProgress* pProgress) {
1196          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));
1197            
1198          if(File.length() < 4) return;          if (!InstrumentFileInfo::isSupportedFile(file)) return;
1199            
1200          try {          try {
1201              if(!strcasecmp(".gig", File.substr(File.length() - 4).c_str())) {              if (pProgress != NULL) {
1202                  if (pProgress != NULL) {                  pProgress->SetStatus(0);
1203                      pProgress->SetStatus(0);                  pProgress->CurrentFile = file;
1204                      pProgress->CurrentFile = File;              }
                 }  
1205    
1206                  AddGigInstruments(DbDir, File, Index, pProgress);              int dirId = GetDirectoryId(DbDir);
1207                if (dirId == -1) throw Exception("Invalid DB directory: " + toEscapedPath(DbDir));
1208    
1209                  if (pProgress != NULL) {              File f = File(file);
1210                      pProgress->SetScannedFileCount(pProgress->GetScannedFileCount() + 1);              if (!f.Exist()) {
1211                  }                  std::stringstream ss;
1212                    ss << "Fail to stat `" << file << "`: " << f.GetErrorMsg();
1213                    throw Exception(ss.str());
1214              }              }
         } catch(Exception e) {  
             std::cerr << e.Message() << std::endl;  
         }  
     }  
1215    
1216      void InstrumentsDb::AddGigInstruments(String DbDir, String File, int Index, ScanProgress* pProgress) {              if (!f.IsFile()) {
1217          dmsg(2,("InstrumentsDb: AddGigInstruments(DbDir=%s,File=%s,Index=%d)\n", DbDir.c_str(), File.c_str(), Index));                  std::stringstream ss;
1218          int dirId = GetDirectoryId(DbDir);                  ss << "`" << file << "` is not a regular file";
1219          if (dirId == -1) throw Exception("Invalid DB directory: " + DbDir);                  throw Exception(ss.str());
1220                }
1221    
1222          struct stat statBuf;              AddInstrumentsFromFilePriv(DbDir, dirId, file, f, Index, pProgress);
         int res = stat(File.c_str(), &statBuf);  
         if (res) {  
             std::stringstream ss;  
             ss << "Fail to stat `" << File << "`: " << strerror(errno);  
             throw Exception(ss.str());  
         }  
1223    
1224          if (!S_ISREG(statBuf.st_mode)) {              if (pProgress != NULL) {
1225              std::stringstream ss;                  pProgress->SetScannedFileCount(pProgress->GetScannedFileCount() + 1);
1226              ss << "`" << File << "` is not a regular file";              }
1227              throw Exception(ss.str());          } catch(Exception e) {
1228                e.PrintMessage();
1229          }          }
1230        }
1231    
1232        void InstrumentsDb::AddInstrumentsFromFilePriv(String DbDir, const int dirId, String FilePath, File file, int Index, ScanProgress* pProgress) {
1233            dmsg(2,("InstrumentsDb: AddGigInstruments(DbDir=%s,FilePath=%s,Index=%d)\n", DbDir.c_str(), FilePath.c_str(), Index));
1234    
1235          RIFF::File* riff = NULL;          bool unlocked = false;
1236          gig::File* gig = NULL;          InstrumentFileInfo* fileInfo = NULL;
1237            sqlite3_stmt* pStmt = NULL;
1238          try {          try {
1239              riff = new RIFF::File(File);              fileInfo = InstrumentFileInfo::getFileInfoFor(FilePath);
1240              gig::File* gig = new gig::File(riff);              if (!fileInfo) return;
1241    
1242              std::stringstream sql;              std::stringstream sql;
1243              sql << "INSERT INTO instruments (dir_id,instr_name,instr_file,";              sql << "INSERT INTO instruments (dir_id,instr_name,instr_file,";
1244              sql << "instr_nr,format_family,format_version,instr_size,";              sql << "instr_nr,format_family,format_version,instr_size,";
1245              sql << "description,is_drum,product,artists,keywords) VALUES (";              sql << "description,is_drum,product,artists,keywords) VALUES (";
1246              sql << dirId << ",?,?,?,'GIG',?," << statBuf.st_size << ",?,?,?,?,?)";              sql << dirId << ",?,?,?,?,?," << file.GetSize() << ",?,?,?,?,?)";
1247    
1248              sqlite3_stmt* pStmt = NULL;              // instr_name 1
1249                // instr_file 2
1250                // instr_nr 3
1251                // format_family 4
1252                // format_version 5
1253                // description 6
1254                // is_drum 7
1255                // product 8
1256                // artists 9
1257                // keywords 10
1258    
1259              int res = sqlite3_prepare(GetDb(), sql.str().c_str(), -1, &pStmt, NULL);              int res = sqlite3_prepare(GetDb(), sql.str().c_str(), -1, &pStmt, NULL);
1260              if (res != SQLITE_OK) {              if (res != SQLITE_OK) {
1261                  throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));                  throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1262              }              }
1263    
1264              BindTextParam(pStmt, 2, File);              BindTextParam(pStmt, 2, toEscapedFsPath(FilePath));
1265              String ver = "";              BindTextParam(pStmt, 4, fileInfo->formatName());
1266              if (gig->pVersion != NULL) ver = ToString(gig->pVersion->major);              BindTextParam(pStmt, 5, fileInfo->formatVersion());
1267              BindTextParam(pStmt, 4, ver);  
1268                int instrIndex = (Index == -1) ? 0 : Index;
1269              if (Index == -1) {  
1270                  int instrIndex = 0;              // Assume that it's locked and should be unlocked at this point
1271                  if (pProgress != NULL) gig->GetInstrument(0, &(pProgress->GigFileProgress)); // TODO: this workaround should be fixed              // to be able to use the database from another threads
1272                  gig::Instrument* pInstrument = gig->GetFirstInstrument();              if (!InTransaction) {
1273                  while (pInstrument) {                  DbInstrumentsMutex.Unlock();
1274                      BindTextParam(pStmt, 7, gig->pInfo->Product);                  unlocked = true;
                     BindTextParam(pStmt, 8, gig->pInfo->Artists);  
                     BindTextParam(pStmt, 9, gig->pInfo->Keywords);  
                     AddGigInstrument(pStmt, DbDir, dirId, File, pInstrument, instrIndex);  
   
                     instrIndex++;  
                     pInstrument = gig->GetNextInstrument();  
                 }  
1275              } else {              } else {
1276                  gig::Instrument* pInstrument;                  std::cerr << "Shouldn't be in transaction when adding instruments." << std::endl;
                 if (pProgress == NULL) pInstrument = gig->GetInstrument(Index);  
                 else pInstrument = gig->GetInstrument(Index, &(pProgress->GigFileProgress));  
                 if (pInstrument != NULL) {  
                     BindTextParam(pStmt, 7, gig->pInfo->Product);  
                     BindTextParam(pStmt, 8, gig->pInfo->Artists);  
                     BindTextParam(pStmt, 9, gig->pInfo->Keywords);  
                     AddGigInstrument(pStmt, DbDir, dirId, File, pInstrument, Index);  
                 }  
1277              }              }
1278    
1279              sqlite3_finalize(pStmt);              optional<InstrumentInfo> info = fileInfo->getInstrumentInfo(0, pProgress);
1280              delete gig;              if (!InTransaction) DbInstrumentsMutex.Lock();
1281              delete riff;              while (info) {
1282          } catch (RIFF::Exception e) {                  String instrumentName = info->instrumentName;
1283              if (gig != NULL) delete gig;                  if (instrumentName.empty())
1284              if (riff != NULL) delete riff;                      instrumentName = Path::getBaseName(FilePath);
1285              std::stringstream ss;                  instrumentName = GetUniqueName(dirId, instrumentName);
1286              ss << "Failed to scan `" << File << "`: " << e.Message;  
1287                                BindTextParam(pStmt, 8, info->product);
1288              throw Exception(ss.str());                  BindTextParam(pStmt, 9, info->artists);
1289          } catch (Exception e) {                  BindTextParam(pStmt, 10, info->keywords);
1290              if (gig != NULL) delete gig;  
1291              if (riff != NULL) delete riff;                  std::stringstream sql2;
1292              throw e;                  sql2 << "SELECT COUNT(*) FROM instruments WHERE instr_file=? AND ";
1293          } catch (...) {                  sql2 << "instr_nr=" << instrIndex;
1294              if (gig != NULL) delete gig;                  String s = toEscapedFsPath(FilePath);
1295              if (riff != NULL) delete riff;                  if (ExecSqlInt(sql2.str(), s) > 0) goto next;
             throw Exception("Failed to scan `" + File + "`");  
         }  
     }  
1296    
1297      void InstrumentsDb::AddGigInstrument(sqlite3_stmt* pStmt, String DbDir, int DirId, String File, gig::Instrument* pInstrument, int Index) {                  BindTextParam(pStmt, 1, instrumentName);
1298          String name = pInstrument->pInfo->Name;                  BindIntParam(pStmt, 3, instrIndex);
         if (name == "") return;  
         name = GetUniqueInstrumentName(DirId, name);  
           
         std::stringstream sql2;  
         sql2 << "SELECT COUNT(*) FROM instruments WHERE instr_file=? AND ";  
         sql2 << "instr_nr=" << Index;  
         if (ExecSqlInt(sql2.str(), File) > 0) return;  
   
         BindTextParam(pStmt, 1, name);  
         BindIntParam(pStmt, 3, Index);  
1299    
1300          BindTextParam(pStmt, 5, pInstrument->pInfo->Comments);                  BindTextParam(pStmt, 6, info->comments);
1301          BindIntParam(pStmt, 6, pInstrument->IsDrum);                  BindIntParam(pStmt, 7, info->isDrum);
1302    
1303          if (!pInstrument->pInfo->Product.empty()) {                  res = sqlite3_step(pStmt);
1304              BindTextParam(pStmt, 7, pInstrument->pInfo->Product);                  if (res != SQLITE_DONE) {
1305          }                      throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1306          if (!pInstrument->pInfo->Artists.empty()) {                  }
1307              BindTextParam(pStmt, 8, pInstrument->pInfo->Artists);                  res = sqlite3_reset(pStmt);
1308          }                  FireInstrumentCountChanged(DbDir);
1309    
1310          if (!pInstrument->pInfo->Keywords.empty()) {              next:
1311              BindTextParam(pStmt, 9, pInstrument->pInfo->Keywords);                  if (Index != -1) break;
         }  
1312    
1313          int res = sqlite3_step(pStmt);                  instrIndex++;
1314          if(res != SQLITE_DONE) {                  info = fileInfo->getInstrumentInfo(instrIndex, pProgress);
1315              sqlite3_finalize(pStmt);              }
1316              throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));          } catch (Exception e) {
1317                if (pStmt) sqlite3_finalize(pStmt);
1318                if (fileInfo) delete fileInfo;
1319                if (unlocked) DbInstrumentsMutex.Lock();
1320                std::stringstream ss;
1321                ss << "Failed to scan `" << FilePath << "`: " << e.Message();
1322                throw Exception(ss.str());
1323            } catch (...) {
1324                if (pStmt) sqlite3_finalize(pStmt);
1325                if (fileInfo) delete fileInfo;
1326                if (unlocked) DbInstrumentsMutex.Lock();
1327                throw Exception("Failed to scan `" + FilePath + "`");
1328          }          }
1329            if (pStmt) sqlite3_finalize(pStmt);
1330          res = sqlite3_reset(pStmt);          if (fileInfo) delete fileInfo;
1331          FireInstrumentCountChanged(DbDir);          if (unlocked) DbInstrumentsMutex.Lock();
1332      }      }
1333    
1334      void InstrumentsDb::DirectoryTreeWalk(String Path, DirectoryHandler* pHandler) {      void InstrumentsDb::DirectoryTreeWalk(String AbstractPath, DirectoryHandler* pHandler) {
1335          int DirId = GetDirectoryId(Path);          int DirId = GetDirectoryId(AbstractPath);
1336          if(DirId == -1) throw Exception("Unknown DB directory: " + Path);          if(DirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(AbstractPath));
1337          DirectoryTreeWalk(pHandler, Path, DirId, 0);          DirectoryTreeWalk(pHandler, AbstractPath, DirId, 0);
1338      }      }
1339    
1340      void InstrumentsDb::DirectoryTreeWalk(DirectoryHandler* pHandler, String Path, int DirId, int Level) {      void InstrumentsDb::DirectoryTreeWalk(DirectoryHandler* pHandler, String AbstractPath, int DirId, int Level) {
1341          if(Level == 1000) throw Exception("Possible infinite loop detected");          if(Level == 1000) throw Exception("Possible infinite loop detected");
1342          pHandler->ProcessDirectory(Path, DirId);          pHandler->ProcessDirectory(AbstractPath, DirId);
1343                    
1344          String s;          String s;
1345          StringListPtr pDirs = GetDirectories(DirId);          StringListPtr pDirs = GetDirectories(DirId);
1346          for(int i = 0; i < pDirs->size(); i++) {          for(int i = 0; i < pDirs->size(); i++) {
1347              if (Path.length() == 1 && Path.at(0) == '/') s = "/" + pDirs->at(i);              if (AbstractPath.length() == 1 && AbstractPath.at(0) == '/') {
1348              else s = Path + "/" + pDirs->at(i);                  s = "/" + pDirs->at(i);
1349                } else {
1350                    s = AbstractPath + "/" + pDirs->at(i);
1351                }
1352              DirectoryTreeWalk(pHandler, s, GetDirectoryId(DirId, pDirs->at(i)), Level + 1);              DirectoryTreeWalk(pHandler, s, GetDirectoryId(DirId, pDirs->at(i)), Level + 1);
1353          }          }
1354      }      }
# Line 1266  namespace LinuxSampler { Line 1360  namespace LinuxSampler {
1360          BeginTransaction();          BeginTransaction();
1361          try {          try {
1362              int DirId = GetDirectoryId(Dir);              int DirId = GetDirectoryId(Dir);
1363              if(DirId == -1) throw Exception("Unknown DB directory: " + Dir);              if(DirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
1364    
1365              if (Recursive) DirectoryTreeWalk(Dir, &directoryFinder);              if (Recursive) DirectoryTreeWalk(Dir, &directoryFinder);
1366              else directoryFinder.ProcessDirectory(Dir, DirId);              else directoryFinder.ProcessDirectory(Dir, DirId);
1367          } catch (Exception e) {          } catch (Exception e) {
1368              EndTransaction();              EndTransaction();
1369              throw e;              throw; // re-throw
1370          }          }
1371          EndTransaction();          EndTransaction();
1372    
# Line 1286  namespace LinuxSampler { Line 1380  namespace LinuxSampler {
1380          BeginTransaction();          BeginTransaction();
1381          try {          try {
1382              int DirId = GetDirectoryId(Dir);              int DirId = GetDirectoryId(Dir);
1383              if(DirId == -1) throw Exception("Unknown DB directory: " + Dir);              if(DirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
1384    
1385              if (Recursive) DirectoryTreeWalk(Dir, &instrumentFinder);              if (Recursive) DirectoryTreeWalk(Dir, &instrumentFinder);
1386              else instrumentFinder.ProcessDirectory(Dir, DirId);              else instrumentFinder.ProcessDirectory(Dir, DirId);
1387          } catch (Exception e) {          } catch (Exception e) {
1388              EndTransaction();              EndTransaction();
1389              throw e;              throw; // re-throw
1390          }          }
1391          EndTransaction();          EndTransaction();
1392    
1393          return instrumentFinder.GetInstruments();          return instrumentFinder.GetInstruments();
1394      }      }
1395        
1396        StringListPtr InstrumentsDb::FindLostInstrumentFiles() {
1397            dmsg(2,("InstrumentsDb: FindLostInstrumentFiles()\n"));
1398    
1399            BeginTransaction();
1400            try {
1401                StringListPtr files = ExecSqlStringList("SELECT DISTINCT instr_file FROM instruments");
1402                StringListPtr result(new std::vector<String>);
1403                for (int i = 0; i < files->size(); i++) {
1404                    File f(toNonEscapedFsPath(files->at(i)));
1405                    if (!f.Exist()) result->push_back(files->at(i));
1406                }
1407                EndTransaction();
1408                return result;
1409            } catch (Exception e) {
1410                EndTransaction();
1411                throw; // re-throw
1412            }
1413        }
1414        
1415        void InstrumentsDb::SetInstrumentFilePath(String OldPath, String NewPath) {
1416            if (OldPath == NewPath) return;
1417            StringListPtr instrs;
1418            BeginTransaction();
1419            try {
1420                std::vector<String> params(2);
1421                params[0] = toEscapedFsPath(NewPath);
1422                params[1] = toEscapedFsPath(OldPath);
1423                instrs = GetInstrumentsByFile(OldPath);
1424                ExecSql("UPDATE instruments SET instr_file=? WHERE instr_file=?", params);
1425            } catch (Exception e) {
1426                EndTransaction();
1427                throw; // re-throw
1428            }
1429            EndTransaction();
1430            
1431            for (int i = 0; i < instrs->size(); i++) {
1432                FireInstrumentInfoChanged(instrs->at(i));
1433            }
1434        }
1435    
1436      void InstrumentsDb::BeginTransaction() {      void InstrumentsDb::BeginTransaction() {
1437          dmsg(2,("InstrumentsDb: BeginTransaction(InTransaction=%d)\n", InTransaction));          dmsg(2,("InstrumentsDb: BeginTransaction(InTransaction=%d)\n", InTransaction));
# Line 1332  namespace LinuxSampler { Line 1466  namespace LinuxSampler {
1466          }          }
1467          InTransaction = false;          InTransaction = false;
1468                    
1469          if(db == NULL) {          if (db == NULL) {
1470              DbInstrumentsMutex.Unlock();              DbInstrumentsMutex.Unlock();
1471              return;              return;
1472          }          }
# Line 1359  namespace LinuxSampler { Line 1493  namespace LinuxSampler {
1493    
1494      void InstrumentsDb::ExecSql(String Sql) {      void InstrumentsDb::ExecSql(String Sql) {
1495          dmsg(2,("InstrumentsDb: ExecSql(Sql=%s)\n", Sql.c_str()));          dmsg(2,("InstrumentsDb: ExecSql(Sql=%s)\n", Sql.c_str()));
1496          sqlite3_stmt *pStmt = NULL;          std::vector<String> Params;
1497                    ExecSql(Sql, Params);
         int res = sqlite3_prepare(GetDb(), Sql.c_str(), -1, &pStmt, NULL);  
         if (res != SQLITE_OK) {  
             throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));  
         }  
           
         res = sqlite3_step(pStmt);  
         if(res != SQLITE_DONE) {  
             sqlite3_finalize(pStmt);  
             throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));  
         }  
   
         sqlite3_finalize(pStmt);  
1498      }      }
1499    
1500      void InstrumentsDb::ExecSql(String Sql, String Param) {      void InstrumentsDb::ExecSql(String Sql, String Param) {
1501          dmsg(2,("InstrumentsDb: ExecSql(Sql=%s,Param=%s)\n", Sql.c_str(), Param.c_str()));          dmsg(2,("InstrumentsDb: ExecSql(Sql=%s,Param=%s)\n", Sql.c_str(), Param.c_str()));
1502            std::vector<String> Params;
1503            Params.push_back(Param);
1504            ExecSql(Sql, Params);
1505        }
1506    
1507        void InstrumentsDb::ExecSql(String Sql, std::vector<String>& Params) {
1508            dmsg(2,("InstrumentsDb: ExecSql(Sql=%s,Params)\n", Sql.c_str()));
1509          sqlite3_stmt *pStmt = NULL;          sqlite3_stmt *pStmt = NULL;
1510                    
1511          int res = sqlite3_prepare(GetDb(), Sql.c_str(), -1, &pStmt, NULL);          int res = sqlite3_prepare(GetDb(), Sql.c_str(), -1, &pStmt, NULL);
# Line 1385  namespace LinuxSampler { Line 1514  namespace LinuxSampler {
1514              throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));              throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1515          }          }
1516    
1517          BindTextParam(pStmt, 1, Param);          for(int i = 0; i < Params.size(); i++) {
1518                BindTextParam(pStmt, i + 1, Params[i]);
1519            }
1520    
1521          res = sqlite3_step(pStmt);          res = sqlite3_step(pStmt);
1522          if (res != SQLITE_DONE) {          if (res != SQLITE_DONE) {
# Line 1467  namespace LinuxSampler { Line 1598  namespace LinuxSampler {
1598      }      }
1599    
1600      IntListPtr InstrumentsDb::ExecSqlIntList(String Sql) {      IntListPtr InstrumentsDb::ExecSqlIntList(String Sql) {
1601            dmsg(2,("InstrumentsDb: ExecSqlIntList(Sql=%s)\n", Sql.c_str()));
1602            std::vector<String> Params;
1603            return ExecSqlIntList(Sql, Params);
1604        }
1605    
1606        IntListPtr InstrumentsDb::ExecSqlIntList(String Sql, String Param) {
1607            dmsg(2,("InstrumentsDb: ExecSqlIntList(Sql=%s,Param=%s)\n", Sql.c_str(), Param.c_str()));
1608            std::vector<String> Params;
1609            Params.push_back(Param);
1610            return ExecSqlIntList(Sql, Params);
1611        }
1612    
1613        IntListPtr InstrumentsDb::ExecSqlIntList(String Sql, std::vector<String>& Params) {
1614            dmsg(2,("InstrumentsDb: ExecSqlIntList(Sql=%s)\n", Sql.c_str()));
1615          IntListPtr intList(new std::vector<int>);          IntListPtr intList(new std::vector<int>);
1616                    
1617          sqlite3_stmt *pStmt = NULL;          sqlite3_stmt *pStmt = NULL;
# Line 1476  namespace LinuxSampler { Line 1621  namespace LinuxSampler {
1621              throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));              throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1622          }          }
1623                    
1624            for(int i = 0; i < Params.size(); i++) {
1625                BindTextParam(pStmt, i + 1, Params[i]);
1626            }
1627            
1628          res = sqlite3_step(pStmt);          res = sqlite3_step(pStmt);
1629          while(res == SQLITE_ROW) {          while(res == SQLITE_ROW) {
1630              intList->push_back(sqlite3_column_int(pStmt, 0));              intList->push_back(sqlite3_column_int(pStmt, 0));
# Line 1493  namespace LinuxSampler { Line 1642  namespace LinuxSampler {
1642      }      }
1643            
1644      StringListPtr InstrumentsDb::ExecSqlStringList(String Sql) {      StringListPtr InstrumentsDb::ExecSqlStringList(String Sql) {
1645            dmsg(2,("InstrumentsDb: ExecSqlStringList(Sql=%s)\n", Sql.c_str()));
1646          StringListPtr stringList(new std::vector<String>);          StringListPtr stringList(new std::vector<String>);
1647                    
1648          sqlite3_stmt *pStmt = NULL;          sqlite3_stmt *pStmt = NULL;
# Line 1520  namespace LinuxSampler { Line 1670  namespace LinuxSampler {
1670    
1671      void InstrumentsDb::BindTextParam(sqlite3_stmt* pStmt, int Index, String Text) {      void InstrumentsDb::BindTextParam(sqlite3_stmt* pStmt, int Index, String Text) {
1672          if (pStmt == NULL) return;          if (pStmt == NULL) return;
1673          int res = sqlite3_bind_text(pStmt, Index, Text.c_str(), -1, SQLITE_STATIC);          int res = sqlite3_bind_text(pStmt, Index, Text.c_str(), -1, SQLITE_TRANSIENT);
1674          if (res != SQLITE_OK) {          if (res != SQLITE_OK) {
1675              sqlite3_finalize(pStmt);              sqlite3_finalize(pStmt);
1676              throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));              throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
# Line 1536  namespace LinuxSampler { Line 1686  namespace LinuxSampler {
1686          }          }
1687      }      }
1688    
1689    #ifndef WIN32
1690      void InstrumentsDb::Regexp(sqlite3_context* pContext, int argc, sqlite3_value** ppValue) {      void InstrumentsDb::Regexp(sqlite3_context* pContext, int argc, sqlite3_value** ppValue) {
1691          if (argc != 2) return;          if (argc != 2) return;
1692    
# Line 1546  namespace LinuxSampler { Line 1697  namespace LinuxSampler {
1697              sqlite3_result_int(pContext, 1);              sqlite3_result_int(pContext, 1);
1698          }          }
1699      }      }
1700    #endif
1701    
1702      String InstrumentsDb::GetDirectoryPath(String File) {      String InstrumentsDb::GetDirectoryPath(String File) {
1703          if (File.empty()) return String("");          if (File.empty()) return String("");
1704          if (File.at(0) != '/') String("");          if (File.at(0) != '/') String("");
1705          if (File.length() == 1) return File;          if (File.length() == 1) return File;
1706          if (File.at(File.length() - 1) == '/') return File.substr(0, File.length() - 1);          if (File.at(File.length() - 1) == '/') return File.substr(0, File.length() - 1);
1707          int i = File.rfind('/', File.length() - 1);          int i = (int) File.rfind('/', File.length() - 1);
1708          if(i == std::string::npos) return String("");          if(i == std::string::npos) return String("");
1709          if(i == 0) return String("/");          if(i == 0) return String("/");
1710          return File.substr(0, i);          return File.substr(0, i);
# Line 1562  namespace LinuxSampler { Line 1714  namespace LinuxSampler {
1714          if (Path.length() < 2) return String("");          if (Path.length() < 2) return String("");
1715          if (Path.at(0) != '/') String("");          if (Path.at(0) != '/') String("");
1716          if (Path.at(Path.length() - 1) == '/') return String("");          if (Path.at(Path.length() - 1) == '/') return String("");
1717          int i = Path.rfind('/', Path.length() - 1);          int i = (int) Path.rfind('/', Path.length() - 1);
1718          return Path.substr(i + 1);          return Path.substr(i + 1);
1719      }      }
1720    
1721      void InstrumentsDb::CheckPathName(String Path) {      void InstrumentsDb::CheckPathName(String Path) {
1722          if (Path.empty()) return;          if (Path.empty()) return;
1723    
1724          int i = 0, j = Path.find('/', i);          int i = 0, j = (int)Path.find('/', i);
1725    
1726          while(j != std::string::npos) {          while(j != std::string::npos) {
1727              if (j + 1 >= Path.length()) return;              if (j + 1 >= Path.length()) return;
1728              if (Path.at(j + 1) == '/') throw Exception("Invalid path name: " + Path);              if (Path.at(j + 1) == '/') throw Exception("Invalid path name: " + Path);
1729                            
1730              i = j + 1;              i = j + 1;
1731              j = Path.find('/', i);              j = (int) Path.find('/', i);
1732          }          }
1733      }      }
1734            
1735      String InstrumentsDb::GetParentDirectory(String Dir) {      String InstrumentsDb::GetParentDirectory(String Dir) {
1736          if (Dir.length() < 2) return String("");          if (Dir.length() < 2) return String("");
1737          if (Dir.at(0) != '/') String("");          if (Dir.at(0) != '/') String("");
1738          int i = Dir.rfind('/', Dir.length() - 2);          int i = (int) Dir.rfind('/', Dir.length() - 2);
1739          if (i == 0) return "/";          if (i == 0) return "/";
1740          return Dir.substr(0, i);          return Dir.substr(0, i);
1741      }      }
1742    
1743        void InstrumentsDb::Format() {
1744            {
1745                LockGuard lock(DbInstrumentsMutex);
1746    
1747                if (db != NULL) {
1748                    sqlite3_close(db);
1749                    db = NULL;
1750                }
1751    
1752                if (DbFile.empty()) DbFile = GetDefaultDBLocation();
1753                String bkp = DbFile + ".bkp";
1754                remove(bkp.c_str());
1755                if (rename(DbFile.c_str(), bkp.c_str()) && errno != ENOENT) {
1756                    throw Exception(String("Failed to backup database: ") + strerror(errno));
1757                }
1758            
1759                String f = DbFile;
1760                DbFile = "";
1761                CreateInstrumentsDb(f);
1762            }
1763            FireDirectoryCountChanged("/");
1764            FireInstrumentCountChanged("/");
1765        }
1766    
1767      void InstrumentsDb::CheckFileName(String File) {      void InstrumentsDb::CheckFileName(String File) {
1768          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);  
         }  
1769      }      }
1770    
1771      String InstrumentsDb::GetUniqueInstrumentName(int DirId, String Name) {      String InstrumentsDb::GetUniqueName(int DirId, String Name) {
1772          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()));
1773    
1774          if (GetInstrumentId(DirId, Name) == -1 && GetDirectoryId(DirId, Name) == -1) return Name;          if (GetInstrumentId(DirId, Name) == -1 && GetDirectoryId(DirId, Name) == -1) return Name;
# Line 1610  namespace LinuxSampler { Line 1783  namespace LinuxSampler {
1783    
1784          throw Exception("Unable to find an unique name: " + Name);          throw Exception("Unable to find an unique name: " + Name);
1785      }      }
1786        
1787        String InstrumentsDb::PrepareSubdirectory(String DbDir, String FsPath) {
1788            std::string dir = Path::getBaseName(FsPath);
1789            dir = toAbstractName(dir);
1790            if(dir.empty()) dir = "New Directory";
1791            dir = GetUniqueName(GetDirectoryId(DbDir), dir);
1792            dir = AppendNode(DbDir, dir);
1793            AddDirectory(dir);
1794            return dir;
1795        }
1796    
1797        String InstrumentsDb::AppendNode(String DbDir, String Node) {
1798            if(DbDir.length() == 1 && DbDir.at(0) == '/') return DbDir + Node;
1799            if(DbDir.at(DbDir.length() - 1) == '/') return DbDir + Node;
1800            return DbDir + "/" + Node;
1801        }
1802    
1803        String InstrumentsDb::toDbName(String AbstractName) {
1804            for (int i = 0; i < AbstractName.length(); i++) {
1805                if (AbstractName.at(i) == '\0') AbstractName.at(i) = '/';
1806            }
1807            return AbstractName;
1808        }
1809    
1810        String InstrumentsDb::toEscapedPath(String AbstractName) {
1811            for (int i = 0; i < AbstractName.length(); i++) {
1812                if (AbstractName.at(i) == '\0')      AbstractName.replace(i++, 1, "\\x2f");
1813                else if (AbstractName.at(i) == '\\') AbstractName.replace(i++, 1, "\\\\");
1814                else if (AbstractName.at(i) == '\'') AbstractName.replace(i++, 1, "\\'");
1815                else if (AbstractName.at(i) == '"')  AbstractName.replace(i++, 1, "\\\"");
1816                else if (AbstractName.at(i) == '\r') AbstractName.replace(i++, 1, "\\r");
1817                else if (AbstractName.at(i) == '\n') AbstractName.replace(i++, 1, "\\n");
1818            }
1819            return AbstractName;
1820        }
1821        
1822        String InstrumentsDb::toEscapedText(String text) {
1823            for (int i = 0; i < text.length(); i++) {
1824                if (text.at(i) == '\\')      text.replace(i++, 1, "\\\\");
1825                else if (text.at(i) == '\'') text.replace(i++, 1, "\\'");
1826                else if (text.at(i) == '"')  text.replace(i++, 1, "\\\"");
1827                else if (text.at(i) == '\r') text.replace(i++, 1, "\\r");
1828                else if (text.at(i) == '\n') text.replace(i++, 1, "\\n");
1829            }
1830            return text;
1831        }
1832        
1833        String InstrumentsDb::toNonEscapedText(String text) {
1834            String sb;
1835            for (int i = 0; i < text.length(); i++) {
1836                char c = text.at(i);
1837                            if(c == '\\') {
1838                                    if(i >= text.length()) {
1839                                            std::cerr << "Broken escape sequence!" << std::endl;
1840                                            break;
1841                                    }
1842                                    char c2 = text.at(++i);
1843                                    if(c2 == '\'')      sb.push_back('\'');
1844                                    else if(c2 == '"')  sb.push_back('"');
1845                                    else if(c2 == '\\') sb.push_back('\\');
1846                                    else if(c2 == 'r')  sb.push_back('\r');
1847                                    else if(c2 == 'n')  sb.push_back('\n');
1848                                    else std::cerr << "Unknown escape sequence \\" << c2 << std::endl;
1849                            } else {
1850                                    sb.push_back(c);
1851                            }
1852            }
1853            return sb;
1854        }
1855        
1856        String InstrumentsDb::toEscapedFsPath(String FsPath) {
1857    #ifdef WIN32
1858            replace(FsPath.begin(), FsPath.end(), '\\', '/');
1859    #endif
1860            return toEscapedText(FsPath);
1861        }
1862        
1863        String InstrumentsDb::toNonEscapedFsPath(String FsPath) {
1864            FsPath = toNonEscapedText(FsPath);
1865    #ifdef WIN32
1866            replace(FsPath.begin(), FsPath.end(), '/', '\\');
1867    #endif
1868            return FsPath;
1869        }
1870        
1871        String InstrumentsDb::toAbstractName(String DbName) {
1872            for (int i = 0; i < DbName.length(); i++) {
1873                if (DbName.at(i) == '/') DbName.at(i) = '\0';
1874            }
1875            return DbName;
1876        }
1877    
1878      void InstrumentsDb::FireDirectoryCountChanged(String Dir) {      void InstrumentsDb::FireDirectoryCountChanged(String Dir) {
1879          for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {          for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {
# Line 1654  namespace LinuxSampler { Line 1918  namespace LinuxSampler {
1918      }      }
1919    
1920  } // namespace LinuxSampler  } // namespace LinuxSampler
   
 #endif // HAVE_SQLITE3  

Legend:
Removed from v.1200  
changed lines
  Added in v.3705

  ViewVC Help
Powered by ViewVC