/[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 1187 by iliev, Wed May 16 14:22:26 2007 UTC revision 1912 by senoner, Sat Jun 6 16:48:00 2009 UTC
# Line 1  Line 1 
1  /***************************************************************************  /***************************************************************************
2   *                                                                         *   *                                                                         *
3   *   Copyright (C) 2007 Grigor Iliev                                       *   *   Copyright (C) 2007-2009 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 <dirent.h>  #include <vector>
30  #include <errno.h>  #include <errno.h>
31    #ifndef WIN32
32  #include <fnmatch.h>  #include <fnmatch.h>
33  #include <ftw.h>  #else
34    #include <direct.h>
35    #endif
36  #include "../common/Exception.h"  #include "../common/Exception.h"
37    
38  namespace LinuxSampler {  namespace LinuxSampler {
39    
40      void DbInstrument::Copy(const DbInstrument& Instr) {      InstrumentsDb InstrumentsDb::instance;
         if (this == &Instr) return;  
41    
42          InstrFile = Instr.InstrFile;      void InstrumentsDb::CreateInstrumentsDb(String FilePath) {
43          InstrNr = Instr.InstrNr;          File f = File(FilePath);
44          FormatFamily = Instr.FormatFamily;          if (f.Exist()) {
45          FormatVersion = Instr.FormatVersion;              throw Exception("File exists: " + FilePath);
         Size = Instr.Size;  
         Created = Instr.Created;  
         Modified = Instr.Modified;  
         Description = Instr.Description;  
         IsDrum = Instr.IsDrum;  
         Product = Instr.Product;  
         Artists = Instr.Artists;  
         Keywords = Instr.Keywords;  
     }  
   
   
     void DbDirectory::Copy(const DbDirectory& Dir) {  
         if (this == &Dir) return;  
   
         Created = Dir.Created;  
         Modified = Dir.Modified;  
         Description = Dir.Description;  
     }  
   
     SearchQuery::SearchQuery() {  
         MinSize = -1;  
         MaxSize = -1;  
         InstrType = BOTH;  
     }  
   
     void SearchQuery::SetFormatFamilies(String s) {  
         if (s.length() == 0) return;  
         int i = 0;  
         int j = s.find(',', 0);  
           
         while (j != std::string::npos) {  
             FormatFamilies.push_back(s.substr(i, j - i));  
             i = j + 1;  
             j = s.find(',', i);  
         }  
           
         if (i < s.length()) FormatFamilies.push_back(s.substr(i));  
     }  
   
     void SearchQuery::SetSize(String s) {  
         String s2 = GetMin(s);  
         if (s2.length() > 0) MinSize = atoll(s2.c_str());  
         else MinSize = -1;  
           
         s2 = GetMax(s);  
         if (s2.length() > 0) MaxSize = atoll(s2.c_str());  
         else MaxSize = -1;  
     }  
   
     void SearchQuery::SetCreated(String s) {  
         CreatedAfter = GetMin(s);  
         CreatedBefore = GetMax(s);  
     }  
   
     void SearchQuery::SetModified(String s) {  
         ModifiedAfter = GetMin(s);  
         ModifiedBefore = GetMax(s);  
     }  
   
     String SearchQuery::GetMin(String s) {  
         if (s.length() < 3) return "";  
         if (s.at(0) == '.' && s.at(1) == '.') return "";  
         int i = s.find("..");  
         if (i == std::string::npos) return "";  
         return s.substr(0, i);  
     }  
   
     String SearchQuery::GetMax(String s) {  
         if (s.length() < 3) return "";  
         if (s.find("..", s.length() - 2) != std::string::npos) return "";  
         int i = s.find("..");  
         if (i == std::string::npos) return "";  
         return s.substr(i + 2);  
     }  
       
     bool InstrumentsDb::AbstractFinder::IsRegex(String Pattern) {  
         if(Pattern.find('?') != String::npos) return true;  
         if(Pattern.find('*') != String::npos) return true;  
         return false;  
     }  
   
     void InstrumentsDb::AbstractFinder::AddSql(String Col, String Pattern, std::stringstream& Sql) {  
         if (Pattern.length() == 0) return;  
   
         if (IsRegex(Pattern)) {  
             Sql << " AND " << Col << " regexp ?";  
             Params.push_back(Pattern);  
             return;  
         }  
   
         String buf;  
         std::vector<String> tokens;  
         std::vector<String> tokens2;  
         std::stringstream ss(Pattern);  
         while (ss >> buf) tokens.push_back(buf);  
   
         if (tokens.size() == 0) {  
             Sql << " AND " << Col << " LIKE ?";  
             Params.push_back("%" + Pattern + "%");  
             return;  
         }  
   
         bool b = false;  
         for (int i = 0; i < tokens.size(); i++) {  
             Sql << (i == 0 ? " AND (" : "");  
   
             for (int j = 0; j < tokens.at(i).length(); j++) {  
                 if (tokens.at(i).at(j) == '+') tokens.at(i).at(j) = ' ';  
             }  
   
             ss.clear();  
             ss.str("");  
             ss << tokens.at(i);  
   
             tokens2.clear();  
             while (ss >> buf) tokens2.push_back(buf);  
   
             if (b && tokens2.size() > 0) Sql << " OR ";  
             if (tokens2.size() > 1) Sql << "(";  
             for (int j = 0; j < tokens2.size(); j++) {  
                 if (j != 0) Sql << " AND ";  
                 Sql << Col << " LIKE ?";  
                 Params.push_back("%" + tokens2.at(j) + "%");  
                 b = true;  
             }  
             if (tokens2.size() > 1) Sql << ")";  
         }  
         if (!b) Sql << "0)";  
         else Sql << ")";  
     }  
   
     InstrumentsDb::DirectoryFinder::DirectoryFinder(SearchQuery* pQuery) : pDirectories(new std::vector<String>) {  
         pStmt = NULL;  
         this->pQuery = pQuery;  
         std::stringstream sql;  
         sql << "SELECT dir_name from instr_dirs WHERE parent_dir_id=?";  
   
         if (pQuery->CreatedAfter.length() != 0) {  
             sql << " AND created > ?";  
             Params.push_back(pQuery->CreatedAfter);  
         }  
         if (pQuery->CreatedBefore.length() != 0) {  
             sql << " AND created < ?";  
             Params.push_back(pQuery->CreatedBefore);  
         }  
         if (pQuery->ModifiedAfter.length() != 0) {  
             sql << " AND modified > ?";  
             Params.push_back(pQuery->ModifiedAfter);  
         }  
         if (pQuery->ModifiedBefore.length() != 0) {  
             sql << " AND modified < ?";  
             Params.push_back(pQuery->ModifiedBefore);  
         }  
   
         AddSql("dir_name", pQuery->Name, sql);  
         AddSql("description", pQuery->Description, sql);  
         SqlQuery = sql.str();  
   
         InstrumentsDb* idb = InstrumentsDb::GetInstrumentsDb();  
   
         int res = sqlite3_prepare(idb->GetDb(), SqlQuery.c_str(), -1, &pStmt, NULL);  
         if (res != SQLITE_OK) {  
             throw Exception("DB error: " + ToString(sqlite3_errmsg(idb->GetDb())));  
         }  
   
         for(int i = 0; i < Params.size(); i++) {  
             idb->BindTextParam(pStmt, i + 2, Params.at(i));  
         }  
     }  
       
     InstrumentsDb::DirectoryFinder::~DirectoryFinder() {  
         if (pStmt != NULL) sqlite3_finalize(pStmt);  
     }  
   
     StringListPtr InstrumentsDb::DirectoryFinder::GetDirectories() {  
         return pDirectories;  
     }  
       
     void InstrumentsDb::DirectoryFinder::ProcessDirectory(String Path, int DirId) {  
         InstrumentsDb* idb = InstrumentsDb::GetInstrumentsDb();  
         idb->BindIntParam(pStmt, 1, DirId);  
   
         String s = Path;  
         if(Path.compare("/") != 0) s += "/";  
         int res = sqlite3_step(pStmt);  
         while(res == SQLITE_ROW) {  
             pDirectories->push_back(s + ToString(sqlite3_column_text(pStmt, 0)));  
             res = sqlite3_step(pStmt);  
46          }          }
47                    
48          if (res != SQLITE_DONE) {          GetInstrumentsDb()->SetDbFile(FilePath);
             sqlite3_finalize(pStmt);  
             throw Exception("DB error: " + ToString(sqlite3_errmsg(idb->GetDb())));  
         }  
   
         res = sqlite3_reset(pStmt);  
         if (res != SQLITE_OK) {  
             sqlite3_finalize(pStmt);  
             throw Exception("DB error: " + ToString(sqlite3_errmsg(idb->GetDb())));  
         }  
     }  
   
     InstrumentsDb::InstrumentFinder::InstrumentFinder(SearchQuery* pQuery) : pInstruments(new std::vector<String>) {  
         pStmt = NULL;  
         this->pQuery = pQuery;  
         std::stringstream sql;  
         sql << "SELECT instr_name from instruments WHERE dir_id=?";  
   
         if (pQuery->CreatedAfter.length() != 0) {  
             sql << " AND created > ?";  
             Params.push_back(pQuery->CreatedAfter);  
         }  
         if (pQuery->CreatedBefore.length() != 0) {  
             sql << " AND created < ?";  
             Params.push_back(pQuery->CreatedBefore);  
         }  
         if (pQuery->ModifiedAfter.length() != 0) {  
             sql << " AND modified > ?";  
             Params.push_back(pQuery->ModifiedAfter);  
         }  
         if (pQuery->ModifiedBefore.length() != 0) {  
             sql << " AND modified < ?";  
             Params.push_back(pQuery->ModifiedBefore);  
         }  
         if (pQuery->MinSize != -1) sql << " AND instr_size > " << pQuery->MinSize;  
         if (pQuery->MaxSize != -1) sql << " AND instr_size < " << pQuery->MaxSize;  
   
         if (pQuery->InstrType == SearchQuery::CHROMATIC) sql << " AND is_drum = 0";  
         else if (pQuery->InstrType == SearchQuery::DRUM) sql << " AND is_drum != 0";  
   
         if (pQuery->FormatFamilies.size() > 0) {  
             sql << " AND (format_family=?";  
             Params.push_back(pQuery->FormatFamilies.at(0));  
             for (int i = 1; i < pQuery->FormatFamilies.size(); i++) {  
                 sql << "OR format_family=?";  
                 Params.push_back(pQuery->FormatFamilies.at(i));  
             }  
             sql << ")";  
         }  
   
         AddSql("instr_name", pQuery->Name, sql);  
         AddSql("description", pQuery->Description, sql);  
         AddSql("product", pQuery->Product, sql);  
         AddSql("artists", pQuery->Artists, sql);  
         AddSql("keywords", pQuery->Keywords, sql);  
         SqlQuery = sql.str();  
   
         InstrumentsDb* idb = InstrumentsDb::GetInstrumentsDb();  
   
         int res = sqlite3_prepare(idb->GetDb(), SqlQuery.c_str(), -1, &pStmt, NULL);  
         if (res != SQLITE_OK) {  
             throw Exception("DB error: " + ToString(sqlite3_errmsg(idb->GetDb())));  
         }  
   
         for(int i = 0; i < Params.size(); i++) {  
             idb->BindTextParam(pStmt, i + 2, Params.at(i));  
         }  
     }  
       
     InstrumentsDb::InstrumentFinder::~InstrumentFinder() {  
         if (pStmt != NULL) sqlite3_finalize(pStmt);  
     }  
       
     void InstrumentsDb::InstrumentFinder::ProcessDirectory(String Path, int DirId) {  
         InstrumentsDb* idb = InstrumentsDb::GetInstrumentsDb();  
         idb->BindIntParam(pStmt, 1, DirId);  
   
         String s = Path;  
         if(Path.compare("/") != 0) s += "/";  
         int res = sqlite3_step(pStmt);  
         while(res == SQLITE_ROW) {  
             pInstruments->push_back(s + ToString(sqlite3_column_text(pStmt, 0)));  
             res = sqlite3_step(pStmt);  
         }  
           
         if (res != SQLITE_DONE) {  
             sqlite3_finalize(pStmt);  
             throw Exception("DB error: " + ToString(sqlite3_errmsg(idb->GetDb())));  
         }  
   
         res = sqlite3_reset(pStmt);  
         if (res != SQLITE_OK) {  
             sqlite3_finalize(pStmt);  
             throw Exception("DB error: " + ToString(sqlite3_errmsg(idb->GetDb())));  
         }  
     }  
   
     StringListPtr InstrumentsDb::InstrumentFinder::GetInstruments() {  
         return pInstruments;  
     }  
   
     void InstrumentsDb::DirectoryCounter::ProcessDirectory(String Path, int DirId) {  
         count += InstrumentsDb::GetInstrumentsDb()->GetDirectoryCount(DirId);  
     }  
   
     void InstrumentsDb::InstrumentCounter::ProcessDirectory(String Path, int DirId) {  
         count += InstrumentsDb::GetInstrumentsDb()->GetInstrumentCount(DirId);  
     }  
   
     InstrumentsDb::DirectoryCopier::DirectoryCopier(String SrcParentDir, String DestDir) {  
         this->SrcParentDir = SrcParentDir;  
         this->DestDir = DestDir;  
   
         if (DestDir.at(DestDir.length() - 1) != '/') {  
             this->DestDir.append("/");  
         }  
         if (SrcParentDir.at(SrcParentDir.length() - 1) != '/') {  
             this->SrcParentDir.append("/");  
         }  
     }  
   
     void InstrumentsDb::DirectoryCopier::ProcessDirectory(String Path, int DirId) {  
         InstrumentsDb* db = InstrumentsDb::GetInstrumentsDb();  
   
         String dir = DestDir;  
         String subdir = Path;  
         if(subdir.length() > SrcParentDir.length()) {  
             subdir = subdir.substr(SrcParentDir.length());  
             dir += subdir;  
             db->AddDirectory(dir);  
         }  
   
         int dstDirId = db->GetDirectoryId(dir);  
         if(dstDirId == -1) throw Exception("Unkown DB directory: " + dir);  
         IntListPtr ids = db->GetInstrumentIDs(DirId);  
         for (int i = 0; i < ids->size(); i++) {  
             String name = db->GetInstrumentName(ids->at(i));  
             db->CopyInstrument(ids->at(i), name, dstDirId, dir);  
         }  
     }  
   
     InstrumentsDb* InstrumentsDb::pInstrumentsDb = new InstrumentsDb;  
   
     void InstrumentsDb::CreateInstrumentsDb(String File) {  
         struct stat statBuf;  
         int res = stat(File.c_str(), &statBuf);  
         if (!res) {  
             throw Exception("File exists: " + File);  
         }  
           
         GetInstrumentsDb()->SetDbFile(File);  
49    
50          String sql =          String sql =
51              "  CREATE TABLE instr_dirs (                                      "              "  CREATE TABLE instr_dirs (                                      "
# Line 395  namespace LinuxSampler { Line 61  namespace LinuxSampler {
61                    
62          GetInstrumentsDb()->ExecSql(sql);          GetInstrumentsDb()->ExecSql(sql);
63    
64          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, '/');";
65          GetInstrumentsDb()->ExecSql(sql);          GetInstrumentsDb()->ExecSql(sql);
66    
67          sql =          sql =
# Line 424  namespace LinuxSampler { Line 90  namespace LinuxSampler {
90    
91      InstrumentsDb::InstrumentsDb() {      InstrumentsDb::InstrumentsDb() {
92          db = NULL;          db = NULL;
         DbInstrumentsMutex = Mutex();  
93          InTransaction = false;          InTransaction = false;
94      }      }
95    
# Line 432  namespace LinuxSampler { Line 97  namespace LinuxSampler {
97          if (db != NULL) sqlite3_close(db);          if (db != NULL) sqlite3_close(db);
98      }      }
99            
     void InstrumentsDb::Destroy() {  
         if (pInstrumentsDb != NULL) {  
             delete pInstrumentsDb;  
             pInstrumentsDb = NULL;  
         }  
     }  
   
100      void InstrumentsDb::AddInstrumentsDbListener(InstrumentsDb::Listener* l) {      void InstrumentsDb::AddInstrumentsDbListener(InstrumentsDb::Listener* l) {
101          llInstrumentsDbListeners.AddListener(l);          llInstrumentsDbListeners.AddListener(l);
102      }      }
# Line 448  namespace LinuxSampler { Line 106  namespace LinuxSampler {
106      }      }
107            
108      InstrumentsDb* InstrumentsDb::GetInstrumentsDb() {      InstrumentsDb* InstrumentsDb::GetInstrumentsDb() {
109          return pInstrumentsDb;          return &instance;
110      }      }
111            
112      void InstrumentsDb::SetDbFile(String File) {      void InstrumentsDb::SetDbFile(String File) {
# Line 464  namespace LinuxSampler { Line 122  namespace LinuxSampler {
122      sqlite3* InstrumentsDb::GetDb() {      sqlite3* InstrumentsDb::GetDb() {
123          if ( db != NULL) return db;          if ( db != NULL) return db;
124    
125          if (DbFile.empty()) DbFile = "/var/lib/linuxsampler/instruments.db";          if (DbFile.empty()) {
126                        #ifndef WIN32
127                        DbFile = CONFIG_DEFAULT_INSTRUMENTS_DB_LOCATION;
128                            #else
129                            char *userprofile = getenv("USERPROFILE");
130                            if(userprofile) {
131                                String DbPath = userprofile;
132                                    DbPath += "\\.linuxsampler";
133                                DbFile = DbPath + "\\instruments.db";
134                                    File InstrumentsDbFile(DbFile);
135                                    // if no DB exists create the subdir and then the DB
136                                    if( !InstrumentsDbFile.Exist() ) {
137                                        _mkdir( DbPath.c_str() );
138                                            // formats the DB, which creates a new instruments.db file
139                                            Format();
140                                    }
141                        }
142                            else {
143                                // in case USERPROFILE is not set (which should not occur)
144                                DbFile = "instruments.db";
145                            }
146                            #endif
147                }
148                    #if defined(__APPLE__)  /* 20071224 Toshi Nagata  */
149                    if (DbFile.find("~") == 0)
150                            DbFile.replace(0, 1, getenv("HOME"));
151                    #endif
152          int rc = sqlite3_open(DbFile.c_str(), &db);          int rc = sqlite3_open(DbFile.c_str(), &db);
153          if (rc) {          if (rc) {
154              sqlite3_close(db);              sqlite3_close(db);
155              db = NULL;              db = NULL;
156              throw Exception("Cannot open instruments database: " + DbFile);              throw Exception("Cannot open instruments database: " + DbFile);
157          }          }
158    #ifndef WIN32
159          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);
160          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."); }
161    #endif
162    
163            // TODO: remove this in the next version
164            try {
165                int i = ExecSqlInt("SELECT parent_dir_id FROM instr_dirs WHERE dir_id=0");
166                // The parent ID of the root directory should be -2 now.
167                if(i != -2) ExecSql("UPDATE instr_dirs SET parent_dir_id=-2 WHERE dir_id=0");
168            } catch(Exception e) { }
169            ////////////////////////////////////////
170                    
171          return db;          return db;
172      }      }
# Line 486  namespace LinuxSampler { Line 180  namespace LinuxSampler {
180                    
181          int count = ExecSqlInt(sql.str());          int count = ExecSqlInt(sql.str());
182    
         // 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--;  
183          return count;          return count;
184      }      }
185    
# Line 510  namespace LinuxSampler { Line 201  namespace LinuxSampler {
201              throw e;              throw e;
202          }          }
203          EndTransaction();          EndTransaction();
204          if (i == -1) throw Exception("Unkown DB directory: " + Dir);          if (i == -1) throw Exception("Unkown DB directory: " + toEscapedPath(Dir));
205                    
206          return i;          return i;
207      }      }
# Line 529  namespace LinuxSampler { Line 220  namespace LinuxSampler {
220          BeginTransaction();          BeginTransaction();
221          try {          try {
222              int dirId = GetDirectoryId(Dir);              int dirId = GetDirectoryId(Dir);
223              if(dirId == -1) throw Exception("Unknown DB directory: " + Dir);              if(dirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
224    
225              StringListPtr pDirs;              StringListPtr pDirs;
226              if (Recursive) {              if (Recursive) {
# Line 552  namespace LinuxSampler { Line 243  namespace LinuxSampler {
243          std::stringstream sql;          std::stringstream sql;
244          sql << "SELECT dir_name FROM instr_dirs ";          sql << "SELECT dir_name FROM instr_dirs ";
245          sql << "WHERE parent_dir_id=" << DirId << " AND dir_id!=0";          sql << "WHERE parent_dir_id=" << DirId << " AND dir_id!=0";
246          return ExecSqlStringList(sql.str());          StringListPtr dirs = ExecSqlStringList(sql.str());
247    
248            for (int i = 0; i < dirs->size(); i++) {
249                for (int j = 0; j < dirs->at(i).length(); j++) {
250                    if (dirs->at(i).at(j) == '/') dirs->at(i).at(j) = '\0';
251                }
252            }
253    
254            return dirs;
255      }      }
256    
257      int InstrumentsDb::GetDirectoryId(String Dir) {      int InstrumentsDb::GetDirectoryId(String Dir) {
# Line 581  namespace LinuxSampler { Line 280  namespace LinuxSampler {
280    
281      int InstrumentsDb::GetDirectoryId(int ParentDirId, String DirName) {      int InstrumentsDb::GetDirectoryId(int ParentDirId, String DirName) {
282          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()));
283            DirName = toDbName(DirName);
284          std::stringstream sql;          std::stringstream sql;
285          sql << "SELECT dir_id FROM instr_dirs WHERE parent_dir_id=";          sql << "SELECT dir_id FROM instr_dirs WHERE parent_dir_id=";
286          sql << ParentDirId << " AND dir_name=?";          sql << ParentDirId << " AND dir_name=?";
287          return ExecSqlInt(sql.str(), DirName);          return ExecSqlInt(sql.str(), DirName);
288      }      }
289    
290        int InstrumentsDb::GetDirectoryId(int InstrId) {
291            dmsg(2,("InstrumentsDb: GetDirectoryId(InstrId=%d)\n", InstrId));
292            std::stringstream sql;
293            sql << "SELECT dir_id FROM instruments WHERE instr_id=" << InstrId;
294            return ExecSqlInt(sql.str());
295        }
296    
297      String InstrumentsDb::GetDirectoryName(int DirId) {      String InstrumentsDb::GetDirectoryName(int DirId) {
298          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);
299          String name = ExecSqlString(sql);          String name = ExecSqlString(sql);
# Line 611  namespace LinuxSampler { Line 318  namespace LinuxSampler {
318                  path = "/" + path;                  path = "/" + path;
319                  break;                  break;
320              }              }
321              path = GetDirectoryName(DirId) + path;              path = GetDirectoryName(DirId) + "/" + path;
322              DirId = GetParentDirectoryId(DirId);              DirId = GetParentDirectoryId(DirId);
323          }          }
324    
# Line 619  namespace LinuxSampler { Line 326  namespace LinuxSampler {
326    
327          return path;          return path;
328      }      }
329        
330        StringListPtr InstrumentsDb::GetInstrumentsByFile(String File) {
331            dmsg(2,("InstrumentsDb: GetInstrumentsByFile(File=%s)\n", File.c_str()));
332    
333            StringListPtr instrs(new std::vector<String>);
334            
335            BeginTransaction();
336            try {
337                File = toEscapedFsPath(File);
338                IntListPtr ids = ExecSqlIntList("SELECT instr_id FROM instruments WHERE instr_file=?", File);
339                
340                for (int i = 0; i < ids->size(); i++) {
341                    String name = GetInstrumentName(ids->at(i));
342                    String dir = GetDirectoryPath(GetDirectoryId(ids->at(i)));
343                    instrs->push_back(dir + name);
344                }
345            } catch (Exception e) {
346                EndTransaction();
347                throw e;
348            }
349            EndTransaction();
350            
351            return instrs;
352        }
353    
354      void InstrumentsDb::AddDirectory(String Dir) {      void InstrumentsDb::AddDirectory(String Dir) {
355          dmsg(2,("InstrumentsDb: AddDirectory(Dir=%s)\n", Dir.c_str()));          dmsg(2,("InstrumentsDb: AddDirectory(Dir=%s)\n", Dir.c_str()));
# Line 633  namespace LinuxSampler { Line 364  namespace LinuxSampler {
364    
365              String dirName = GetFileName(Dir);              String dirName = GetFileName(Dir);
366              if(ParentDir.empty() || dirName.empty()) {              if(ParentDir.empty() || dirName.empty()) {
367                  throw Exception("Failed to add DB directory: " + Dir);                  throw Exception("Failed to add DB directory: " + toEscapedPath(Dir));
368              }              }
369    
370              int id = GetDirectoryId(ParentDir);              int id = GetDirectoryId(ParentDir);
371              if (id == -1) throw Exception("DB directory doesn't exist: " + ParentDir);              if (id == -1) throw Exception("DB directory doesn't exist: " + toEscapedPath(ParentDir));
372              int id2 = GetDirectoryId(id, dirName);              int id2 = GetDirectoryId(id, dirName);
373              if (id2 != -1) throw Exception("DB directory already exist: " + Dir);              if (id2 != -1) throw Exception("DB directory already exist: " + toEscapedPath(Dir));
374              id2 = GetInstrumentId(id, dirName);              id2 = GetInstrumentId(id, dirName);
375              if (id2 != -1) throw Exception("Instrument with that name exist: " + Dir);              if (id2 != -1) throw Exception("Instrument with that name exist: " + toEscapedPath(Dir));
376    
377              std::stringstream sql;              std::stringstream sql;
378              sql << "INSERT INTO instr_dirs (parent_dir_id, dir_name) VALUES (";              sql << "INSERT INTO instr_dirs (parent_dir_id, dir_name) VALUES (";
379              sql << id << ", ?)";              sql << id << ", ?)";
380    
381              ExecSql(sql.str(), dirName);              ExecSql(sql.str(), toDbName(dirName));
382          } catch (Exception e) {          } catch (Exception e) {
383              EndTransaction();              EndTransaction();
384              throw e;              throw e;
# Line 666  namespace LinuxSampler { Line 397  namespace LinuxSampler {
397          BeginTransaction();          BeginTransaction();
398          try {          try {
399              int dirId = GetDirectoryId(Dir);              int dirId = GetDirectoryId(Dir);
400              if (dirId == -1) throw Exception("Unknown DB directory: " + Dir);              if (dirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
401              if (dirId == 0) throw Exception("Cannot delete the root directory: " + Dir);              if (dirId == 0) throw Exception("Cannot delete the root directory: " + Dir);
402              if(ParentDir.empty()) throw Exception("Unknown parent directory");              if(ParentDir.empty()) throw Exception("Unknown parent directory");
403              if (Force) RemoveDirectoryContent(dirId);              if (Force) RemoveDirectoryContent(dirId);
# Line 753  namespace LinuxSampler { Line 484  namespace LinuxSampler {
484    
485          try {          try {
486              int id = GetDirectoryId(Dir);              int id = GetDirectoryId(Dir);
487              if(id == -1) throw Exception("Unknown DB directory: " + Dir);              if(id == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
488    
489              sqlite3_stmt *pStmt = NULL;              sqlite3_stmt *pStmt = NULL;
490              std::stringstream sql;              std::stringstream sql;
# Line 776  namespace LinuxSampler { Line 507  namespace LinuxSampler {
507                  if (res != SQLITE_DONE) {                  if (res != SQLITE_DONE) {
508                      throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));                      throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
509                  } else {                  } else {
510                      throw Exception("Unknown DB directory: " + Dir);                      throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
511                  }                  }
512              }              }
513                            
# Line 793  namespace LinuxSampler { Line 524  namespace LinuxSampler {
524      void InstrumentsDb::RenameDirectory(String Dir, String Name) {      void InstrumentsDb::RenameDirectory(String Dir, String Name) {
525          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()));
526          CheckFileName(Name);          CheckFileName(Name);
527            String dbName = toDbName(Name);
528    
529          BeginTransaction();          BeginTransaction();
530          try {          try {
531              int dirId = GetDirectoryId(Dir);              int dirId = GetDirectoryId(Dir);
532              if (dirId == -1) throw Exception("Unknown DB directory: " + Dir);              if (dirId == -1) throw Exception("Unknown DB directory: " + toEscapedText(Dir));
533    
534              std::stringstream sql;              std::stringstream sql;
535              sql << "SELECT parent_dir_id FROM instr_dirs WHERE dir_id=" <<  dirId;              sql << "SELECT parent_dir_id FROM instr_dirs WHERE dir_id=" <<  dirId;
536    
537              int parent = ExecSqlInt(sql.str());              int parent = ExecSqlInt(sql.str());
538              if (parent == -1) throw Exception("Unknown parent directory: " + Dir);              if (parent == -1) throw Exception("Unknown parent directory: " + toEscapedPath(Dir));
539              if (GetDirectoryId(parent, Name) != -1) {  
540                  throw Exception("Cannot rename. Directory with that name already exists: " + Name);              if (GetDirectoryId(parent, dbName) != -1) {
541                    String s = toEscapedPath(Name);
542                    throw Exception("Cannot rename. Directory with that name already exists: " + s);
543              }              }
544    
545              if (GetInstrumentId(parent, Name) != -1) {              if (GetInstrumentId(parent, dbName) != -1) {
546                  throw Exception("Cannot rename. Instrument with that name exist: " + Dir);                  throw Exception("Cannot rename. Instrument with that name exist: " + toEscapedPath(Dir));
547              }              }
548    
549              sql.str("");              sql.str("");
550              sql << "UPDATE instr_dirs SET dir_name=? WHERE dir_id=" << dirId;              sql << "UPDATE instr_dirs SET dir_name=? WHERE dir_id=" << dirId;
551              ExecSql(sql.str(), Name);              ExecSql(sql.str(), dbName);
552          } catch (Exception e) {          } catch (Exception e) {
553              EndTransaction();              EndTransaction();
554              throw e;              throw e;
555          }          }
556    
557          EndTransaction();          EndTransaction();
558          FireDirectoryNameChanged(Dir, Name);          FireDirectoryNameChanged(Dir, toAbstractName(Name));
559      }      }
560    
561      void InstrumentsDb::MoveDirectory(String Dir, String Dst) {      void InstrumentsDb::MoveDirectory(String Dir, String Dst) {
# Line 834  namespace LinuxSampler { Line 568  namespace LinuxSampler {
568          BeginTransaction();          BeginTransaction();
569          try {          try {
570              int dirId = GetDirectoryId(Dir);              int dirId = GetDirectoryId(Dir);
571              if (dirId == -1) throw Exception("Unknown DB directory: " + Dir);              if (dirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
572              int dstId = GetDirectoryId(Dst);              int dstId = GetDirectoryId(Dst);
573              if (dstId == -1) throw Exception("Unknown DB directory: " + Dst);              if (dstId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dst));
574              if (dirId == dstId) {              if (dirId == dstId) {
575                  throw Exception("Cannot move directory to itself");                  throw Exception("Cannot move directory to itself");
576              }              }
# Line 852  namespace LinuxSampler { Line 586  namespace LinuxSampler {
586              String dirName = GetFileName(Dir);              String dirName = GetFileName(Dir);
587    
588              int id2 = GetDirectoryId(dstId, dirName);              int id2 = GetDirectoryId(dstId, dirName);
589              if (id2 != -1) throw Exception("DB directory already exist: " + dirName);              if (id2 != -1) throw Exception("DB directory already exist: " + toEscapedPath(dirName));
590              id2 = GetInstrumentId(dstId, dirName);              id2 = GetInstrumentId(dstId, dirName);
591              if (id2 != -1) throw Exception("Instrument with that name exist: " + dirName);              if (id2 != -1) throw Exception("Instrument with that name exist: " + toEscapedPath(dirName));
592    
593              std::stringstream sql;              std::stringstream sql;
594              sql << "UPDATE instr_dirs SET parent_dir_id=" << dstId;              sql << "UPDATE instr_dirs SET parent_dir_id=" << dstId;
# Line 880  namespace LinuxSampler { Line 614  namespace LinuxSampler {
614          BeginTransaction();          BeginTransaction();
615          try {          try {
616              int dirId = GetDirectoryId(Dir);              int dirId = GetDirectoryId(Dir);
617              if (dirId == -1) throw Exception("Unknown DB directory: " + Dir);              if (dirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
618              int dstId = GetDirectoryId(Dst);              int dstId = GetDirectoryId(Dst);
619              if (dstId == -1) throw Exception("Unknown DB directory: " + Dst);              if (dstId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dst));
620              if (dirId == dstId) {              if (dirId == dstId) {
621                  throw Exception("Cannot copy directory to itself");                  throw Exception("Cannot copy directory to itself");
622              }              }
# Line 898  namespace LinuxSampler { Line 632  namespace LinuxSampler {
632              String dirName = GetFileName(Dir);              String dirName = GetFileName(Dir);
633    
634              int id2 = GetDirectoryId(dstId, dirName);              int id2 = GetDirectoryId(dstId, dirName);
635              if (id2 != -1) throw Exception("DB directory already exist: " + dirName);              if (id2 != -1) throw Exception("DB directory already exist: " + toEscapedPath(dirName));
636              id2 = GetInstrumentId(dstId, dirName);              id2 = GetInstrumentId(dstId, dirName);
637              if (id2 != -1) throw Exception("Instrument with that name exist: " + dirName);              if (id2 != -1) throw Exception("Instrument with that name exist: " + toEscapedPath(dirName));
638    
639              DirectoryCopier directoryCopier(ParentDir, Dst);              DirectoryCopier directoryCopier(ParentDir, Dst);
640              DirectoryTreeWalk(Dir, &directoryCopier);              DirectoryTreeWalk(Dir, &directoryCopier);
# Line 918  namespace LinuxSampler { Line 652  namespace LinuxSampler {
652          BeginTransaction();          BeginTransaction();
653          try {          try {
654              int id = GetDirectoryId(Dir);              int id = GetDirectoryId(Dir);
655              if(id == -1) throw Exception("Unknown DB directory: " + Dir);              if(id == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
656    
657              std::stringstream sql;              std::stringstream sql;
658              sql << "UPDATE instr_dirs SET description=?,modified=CURRENT_TIMESTAMP ";              sql << "UPDATE instr_dirs SET description=?,modified=CURRENT_TIMESTAMP ";
# Line 934  namespace LinuxSampler { Line 668  namespace LinuxSampler {
668          FireDirectoryInfoChanged(Dir);          FireDirectoryInfoChanged(Dir);
669      }      }
670    
671      void InstrumentsDb::AddInstruments(String DbDir, String FilePath, int Index) {      int InstrumentsDb::AddInstruments(ScanMode Mode, String DbDir, String FsDir, bool bBackground, bool insDir) {
672          dmsg(2,("InstrumentsDb: AddInstruments(DbDir=%s,FilePath=%s,Index=%d)\n", DbDir.c_str(), FilePath.c_str(), Index));          dmsg(2,("InstrumentsDb: AddInstruments(Mode=%d,DbDir=%s,FsDir=%s,bBackground=%d,insDir=%d)\n", Mode, DbDir.c_str(), FsDir.c_str(), bBackground, insDir));
673            if(!bBackground) {
674                switch (Mode) {
675                    case NON_RECURSIVE:
676                        AddInstrumentsNonrecursive(DbDir, FsDir, insDir);
677                        break;
678                    case RECURSIVE:
679                        AddInstrumentsRecursive(DbDir, FsDir, false, insDir);
680                        break;
681                    case FLAT:
682                        AddInstrumentsRecursive(DbDir, FsDir, true, insDir);
683                        break;
684                    default:
685                        throw Exception("Unknown scan mode");
686                }
687    
688                return -1;
689            }
690    
691            ScanJob job;
692            int jobId = Jobs.AddJob(job);
693            InstrumentsDbThread.Execute(new AddInstrumentsJob(jobId, Mode, DbDir, FsDir, insDir));
694    
695            return jobId;
696        }
697        
698        int InstrumentsDb::AddInstruments(String DbDir, String FilePath, int Index, bool bBackground) {
699            dmsg(2,("InstrumentsDb: AddInstruments(DbDir=%s,FilePath=%s,Index=%d,bBackground=%d)\n", DbDir.c_str(), FilePath.c_str(), Index, bBackground));
700            if(!bBackground) {
701                AddInstruments(DbDir, false, FilePath, Index);
702                return -1;
703            }
704    
705            ScanJob job;
706            int jobId = Jobs.AddJob(job);
707            InstrumentsDbThread.Execute(new AddInstrumentsFromFileJob(jobId, DbDir, FilePath, Index, false));
708    
709            return jobId;
710        }
711    
712        void InstrumentsDb::AddInstruments(String DbDir, bool insDir, String FilePath, int Index, ScanProgress* pProgress) {
713            dmsg(2,("InstrumentsDb: AddInstruments(DbDir=%s,insDir=%d,FilePath=%s,Index=%d)\n", DbDir.c_str(), insDir, FilePath.c_str(), Index));
714          if (DbDir.empty() || FilePath.empty()) return;          if (DbDir.empty() || FilePath.empty()) return;
715                    
716          DbInstrumentsMutex.Lock();          DbInstrumentsMutex.Lock();
717          try {          try {
718              int dirId = GetDirectoryId(DbDir);              int dirId = GetDirectoryId(DbDir);
719              if (dirId == -1) throw Exception("Invalid DB directory: " + DbDir);              if (dirId == -1) throw Exception("Invalid DB directory: " + toEscapedText(DbDir));
720    
721              struct stat statBuf;              File f = File(FilePath);
722              int res = stat(FilePath.c_str(), &statBuf);              if (!f.Exist()) {
             if (res) {  
723                  std::stringstream ss;                  std::stringstream ss;
724                  ss << "Fail to stat `" << FilePath << "`: " << strerror(errno);                  ss << "Fail to stat `" << FilePath << "`: " << f.GetErrorMsg();
725                  throw Exception(ss.str());                  throw Exception(ss.str());
726              }              }
727    
728              if (S_ISREG(statBuf.st_mode)) {              if (!f.IsFile()) {
                 AddInstrumentsFromFile(DbDir, FilePath, Index);  
                 DbInstrumentsMutex.Unlock();  
                 return;  
             }  
   
             if (!S_ISDIR(statBuf.st_mode)) {  
                 DbInstrumentsMutex.Unlock();  
                 return;  
             }  
               
             if (Index != -1) {  
729                  std::stringstream ss;                  std::stringstream ss;
730                  ss << "`" << FilePath << "` is directory, not an instrument file";                  ss << "`" << FilePath << "` is not an instrument file";
731                  throw Exception(ss.str());                  throw Exception(ss.str());
732              }              }
733            
734              AddInstrumentsRecursive(DbDir, FilePath, false);              String dir = insDir ? PrepareSubdirectory(DbDir, FilePath) : DbDir;
735                AddInstrumentsFromFile(dir, FilePath, Index, pProgress);
736          } catch (Exception e) {          } catch (Exception e) {
737              DbInstrumentsMutex.Unlock();              DbInstrumentsMutex.Unlock();
738              throw e;              throw e;
# Line 977  namespace LinuxSampler { Line 741  namespace LinuxSampler {
741          DbInstrumentsMutex.Unlock();          DbInstrumentsMutex.Unlock();
742      }      }
743    
744      void InstrumentsDb::AddInstrumentsNonrecursive(String DbDir, String FsDir) {      void InstrumentsDb::AddInstrumentsNonrecursive(String DbDir, String FsDir, bool insDir, ScanProgress* pProgress) {
745          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));
746          if (DbDir.empty() || FsDir.empty()) return;          if (DbDir.empty() || FsDir.empty()) return;
747                    
748          DbInstrumentsMutex.Lock();          DbInstrumentsMutex.Lock();
749          try {          try {
750              int dirId = GetDirectoryId(DbDir);              int dirId = GetDirectoryId(DbDir);
751              if (dirId == -1) throw Exception("Invalid DB directory: " + DbDir);              if (dirId == -1) throw Exception("Invalid DB directory: " + toEscapedPath(DbDir));
752    
753              struct stat statBuf;              File f = File(FsDir);
754              int res = stat(FsDir.c_str(), &statBuf);              if (!f.Exist()) {
             if (res) {  
755                  std::stringstream ss;                  std::stringstream ss;
756                  ss << "Fail to stat `" << FsDir << "`: " << strerror(errno);                  ss << "Fail to stat `" << FsDir << "`: " << f.GetErrorMsg();
757                  throw Exception(ss.str());                  throw Exception(ss.str());
758              }              }
759    
760              if (!S_ISDIR(statBuf.st_mode)) {              if (!f.IsDirectory()) {
761                  throw Exception("Directory expected");                  throw Exception("Directory expected: " + FsDir);
762              }              }
763                            
764              if (FsDir.at(FsDir.length() - 1) != '/') FsDir.append("/");              if (FsDir.at(FsDir.length() - 1) != File::DirSeparator) {
765                    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;  
766              }              }
767                
768              struct dirent* pEnt = readdir(pDir);              try {
769              while (pEnt != NULL) {                  FileListPtr fileList = File::GetFiles(FsDir);
770                  if (pEnt->d_type != DT_REG) {                  for (int i = 0; i < fileList->size(); i++) {
771                      pEnt = readdir(pDir);                      String dir = insDir ? PrepareSubdirectory(DbDir, fileList->at(i)) : DbDir;
772                      continue;                                          AddInstrumentsFromFile(dir, FsDir + fileList->at(i), -1, pProgress);
773                  }                  }
774                } catch(Exception e) {
775                  AddInstrumentsFromFile(DbDir, FsDir + String(pEnt->d_name));                  e.PrintMessage();
776                  pEnt = readdir(pDir);                  DbInstrumentsMutex.Unlock();
777              }                  return;
   
             if (closedir(pDir)) {  
                 std::stringstream ss;  
                 ss << "Failed to close directory `" << FsDir << "`: ";  
                 ss << strerror(errno);  
                 std::cerr << ss.str();  
778              }              }
779          } catch (Exception e) {          } catch (Exception e) {
780              DbInstrumentsMutex.Unlock();              DbInstrumentsMutex.Unlock();
# Line 1035  namespace LinuxSampler { Line 784  namespace LinuxSampler {
784          DbInstrumentsMutex.Unlock();          DbInstrumentsMutex.Unlock();
785      }      }
786    
787      void InstrumentsDb::AddInstrumentsRecursive(String DbDir, String FsDir, bool Flat) {      void InstrumentsDb::AddInstrumentsRecursive(String DbDir, String FsDir, bool Flat, bool insDir, ScanProgress* pProgress) {
788          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));
789          DirectoryScanner::Scan(DbDir, FsDir, Flat);          if (pProgress != NULL) {
790                InstrumentFileCounter c;
791                pProgress->SetTotalFileCount(c.Count(FsDir));
792            }
793    
794            DirectoryScanner d;
795            d.Scan(DbDir, FsDir, Flat, insDir, pProgress);
796      }      }
797    
798      int InstrumentsDb::GetInstrumentCount(int DirId) {      int InstrumentsDb::GetInstrumentCount(int DirId) {
# Line 1069  namespace LinuxSampler { Line 824  namespace LinuxSampler {
824          }          }
825          EndTransaction();          EndTransaction();
826    
827          if (i == -1) throw Exception("Unknown Db directory: " + Dir);          if (i == -1) throw Exception("Unknown Db directory: " + toEscapedPath(Dir));
828          return i;          return i;
829      }      }
830    
# Line 1085  namespace LinuxSampler { Line 840  namespace LinuxSampler {
840          BeginTransaction();          BeginTransaction();
841          try {          try {
842              int dirId = GetDirectoryId(Dir);              int dirId = GetDirectoryId(Dir);
843              if(dirId == -1) throw Exception("Unknown DB directory: " + Dir);              if(dirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
844    
845              StringListPtr pInstrs;              StringListPtr pInstrs;
846    
# Line 1099  namespace LinuxSampler { Line 854  namespace LinuxSampler {
854                  sql << "SELECT instr_name FROM instruments WHERE dir_id=" << dirId;                  sql << "SELECT instr_name FROM instruments WHERE dir_id=" << dirId;
855    
856                  pInstrs = ExecSqlStringList(sql.str());                  pInstrs = ExecSqlStringList(sql.str());
857                    // Converting to abstract names
858                    for (int i = 0; i < pInstrs->size(); i++) {
859                        for (int j = 0; j < pInstrs->at(i).length(); j++) {
860                            if (pInstrs->at(i).at(j) == '/') pInstrs->at(i).at(j) = '\0';
861                        }
862                    }
863              }              }
864              EndTransaction();              EndTransaction();
865              return pInstrs;              return pInstrs;
# Line 1123  namespace LinuxSampler { Line 884  namespace LinuxSampler {
884          std::stringstream sql;          std::stringstream sql;
885          sql << "SELECT instr_id FROM instruments WHERE dir_id=";          sql << "SELECT instr_id FROM instruments WHERE dir_id=";
886          sql << DirId << " AND instr_name=?";          sql << DirId << " AND instr_name=?";
887          return ExecSqlInt(sql.str(), InstrName);          return ExecSqlInt(sql.str(), toDbName(InstrName));
888      }      }
889    
890      String InstrumentsDb::GetInstrumentName(int InstrId) {      String InstrumentsDb::GetInstrumentName(int InstrId) {
891          dmsg(2,("InstrumentsDb: GetInstrumentName(InstrId=%d)\n", InstrId));          dmsg(2,("InstrumentsDb: GetInstrumentName(InstrId=%d)\n", InstrId));
892          std::stringstream sql;          std::stringstream sql;
893          sql << "SELECT instr_name FROM instruments WHERE instr_id=" << InstrId;          sql << "SELECT instr_name FROM instruments WHERE instr_id=" << InstrId;
894          return ExecSqlString(sql.str());          return toAbstractName(ExecSqlString(sql.str()));
895      }      }
896            
897      void InstrumentsDb::RemoveInstrument(String Instr) {      void InstrumentsDb::RemoveInstrument(String Instr) {
# Line 1142  namespace LinuxSampler { Line 903  namespace LinuxSampler {
903          try {          try {
904              int instrId = GetInstrumentId(Instr);              int instrId = GetInstrumentId(Instr);
905              if(instrId == -1) {              if(instrId == -1) {
906                  throw Exception("The specified instrument does not exist: " + Instr);                  throw Exception("The specified instrument does not exist: " + toEscapedPath(Instr));
907              }              }
908              RemoveInstrument(instrId);              RemoveInstrument(instrId);
909          } catch (Exception e) {          } catch (Exception e) {
# Line 1177  namespace LinuxSampler { Line 938  namespace LinuxSampler {
938          BeginTransaction();          BeginTransaction();
939          try {          try {
940              int id = GetInstrumentId(Instr);              int id = GetInstrumentId(Instr);
941              if(id == -1) throw Exception("Unknown DB instrument: " + Instr);              if(id == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
942              i = GetInstrumentInfo(id);              i = GetInstrumentInfo(id);
943          } catch (Exception e) {          } catch (Exception e) {
944              EndTransaction();              EndTransaction();
# Line 1236  namespace LinuxSampler { Line 997  namespace LinuxSampler {
997          BeginTransaction();          BeginTransaction();
998          try {          try {
999              int dirId = GetDirectoryId(GetDirectoryPath(Instr));              int dirId = GetDirectoryId(GetDirectoryPath(Instr));
1000              if (dirId == -1) throw Exception("Unknown DB instrument: " + Instr);              if (dirId == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
1001    
1002              int instrId = GetInstrumentId(dirId, GetFileName(Instr));              int instrId = GetInstrumentId(dirId, GetFileName(Instr));
1003              if (instrId == -1) throw Exception("Unknown DB instrument: " + Instr);              if (instrId == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
1004    
1005              if (GetInstrumentId(dirId, Name) != -1) {              if (GetInstrumentId(dirId, Name) != -1) {
1006                  throw Exception("Cannot rename. Instrument with that name already exists: " + Name);                  String s = toEscapedPath(Name);
1007                    throw Exception("Cannot rename. Instrument with that name already exists: " + s);
1008              }              }
1009    
1010              if (GetDirectoryId(dirId, Name) != -1) {              if (GetDirectoryId(dirId, Name) != -1) {
1011                  throw Exception("Cannot rename. Directory with that name already exists: " + Name);                  String s = toEscapedPath(Name);
1012                    throw Exception("Cannot rename. Directory with that name already exists: " + s);
1013              }              }
1014    
1015              std::stringstream sql;              std::stringstream sql;
1016              sql << "UPDATE instruments SET instr_name=? WHERE instr_id=" << instrId;              sql << "UPDATE instruments SET instr_name=? WHERE instr_id=" << instrId;
1017              ExecSql(sql.str(), Name);              ExecSql(sql.str(), toDbName(Name));
1018          } catch (Exception e) {          } catch (Exception e) {
1019              EndTransaction();              EndTransaction();
1020              throw e;              throw e;
1021          }          }
1022          EndTransaction();          EndTransaction();
1023          FireInstrumentNameChanged(Instr, Name);          FireInstrumentNameChanged(Instr, toAbstractName(Name));
1024      }      }
1025    
1026      void InstrumentsDb::MoveInstrument(String Instr, String Dst) {      void InstrumentsDb::MoveInstrument(String Instr, String Dst) {
# Line 1267  namespace LinuxSampler { Line 1030  namespace LinuxSampler {
1030    
1031          BeginTransaction();          BeginTransaction();
1032          try {          try {
1033              int dirId = GetDirectoryId(GetDirectoryPath(Instr));              int dirId = GetDirectoryId(ParentDir);
1034              if (dirId == -1) throw Exception("Unknown DB instrument: " + Instr);              if (dirId == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
1035    
1036              String instrName = GetFileName(Instr);              String instrName = GetFileName(Instr);
1037              int instrId = GetInstrumentId(dirId, instrName);              int instrId = GetInstrumentId(dirId, instrName);
1038              if (instrId == -1) throw Exception("Unknown DB instrument: " + Instr);              if (instrId == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
1039    
1040              int dstId = GetDirectoryId(Dst);              int dstId = GetDirectoryId(Dst);
1041              if (dstId == -1) throw Exception("Unknown DB directory: " + Dst);              if (dstId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dst));
1042              if (dirId == dstId) {              if (dirId == dstId) {
1043                  EndTransaction();                  EndTransaction();
1044                  return;                  return;
1045              }              }
1046    
1047              if (GetInstrumentId(dstId, instrName) != -1) {              if (GetInstrumentId(dstId, instrName) != -1) {
1048                  throw Exception("Cannot move. Instrument with that name already exists: " + instrName);                  String s = toEscapedPath(instrName);
1049                    throw Exception("Cannot move. Instrument with that name already exists: " + s);
1050              }              }
1051    
1052              if (GetDirectoryId(dstId, instrName) != -1) {              if (GetDirectoryId(dstId, instrName) != -1) {
1053                  throw Exception("Cannot move. Directory with that name already exists: " + instrName);                  String s = toEscapedPath(instrName);
1054                    throw Exception("Cannot move. Directory with that name already exists: " + s);
1055              }              }
1056    
1057              std::stringstream sql;              std::stringstream sql;
# Line 1310  namespace LinuxSampler { Line 1075  namespace LinuxSampler {
1075          BeginTransaction();          BeginTransaction();
1076          try {          try {
1077              int dirId = GetDirectoryId(GetDirectoryPath(Instr));              int dirId = GetDirectoryId(GetDirectoryPath(Instr));
1078              if (dirId == -1) throw Exception("Unknown DB instrument: " + Instr);              if (dirId == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
1079    
1080              String instrName = GetFileName(Instr);              String instrName = GetFileName(Instr);
1081              int instrId = GetInstrumentId(dirId, instrName);              int instrId = GetInstrumentId(dirId, instrName);
1082              if (instrId == -1) throw Exception("Unknown DB instrument: " + Instr);              if (instrId == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
1083    
1084              int dstId = GetDirectoryId(Dst);              int dstId = GetDirectoryId(Dst);
1085              if (dstId == -1) throw Exception("Unknown DB directory: " + Dst);              if (dstId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dst));
1086              if (dirId == dstId) {              if (dirId == dstId) {
1087                  EndTransaction();                  EndTransaction();
1088                  return;                  return;
1089              }              }
1090    
             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);  
             }  
   
1091              CopyInstrument(instrId, instrName, dstId, Dst);              CopyInstrument(instrId, instrName, dstId, Dst);
1092          } catch (Exception e) {          } catch (Exception e) {
1093              EndTransaction();              EndTransaction();
# Line 1341  namespace LinuxSampler { Line 1098  namespace LinuxSampler {
1098      }      }
1099    
1100      void InstrumentsDb::CopyInstrument(int InstrId, String InstrName, int DstDirId, String DstDir) {      void InstrumentsDb::CopyInstrument(int InstrId, String InstrName, int DstDirId, String DstDir) {
1101            if (GetInstrumentId(DstDirId, InstrName) != -1) {
1102                String s = toEscapedPath(InstrName);
1103                throw Exception("Cannot copy. Instrument with that name already exists: " + s);
1104            }
1105    
1106            if (GetDirectoryId(DstDirId, InstrName) != -1) {
1107                String s = toEscapedPath(InstrName);
1108                throw Exception("Cannot copy. Directory with that name already exists: " + s);
1109            }
1110    
1111          DbInstrument i = GetInstrumentInfo(InstrId);          DbInstrument i = GetInstrumentInfo(InstrId);
1112          sqlite3_stmt *pStmt = NULL;          sqlite3_stmt *pStmt = NULL;
1113          std::stringstream sql;          std::stringstream sql;
# Line 1354  namespace LinuxSampler { Line 1121  namespace LinuxSampler {
1121              throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));              throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1122          }          }
1123    
1124          BindTextParam(pStmt, 1, InstrName);          String s = toDbName(InstrName);
1125            BindTextParam(pStmt, 1, s);
1126          BindTextParam(pStmt, 2, i.InstrFile);          BindTextParam(pStmt, 2, i.InstrFile);
1127          BindTextParam(pStmt, 3, i.FormatFamily);          BindTextParam(pStmt, 3, i.FormatFamily);
1128          BindTextParam(pStmt, 4, i.FormatVersion);          BindTextParam(pStmt, 4, i.FormatVersion);
# Line 1379  namespace LinuxSampler { Line 1147  namespace LinuxSampler {
1147          BeginTransaction();          BeginTransaction();
1148          try {          try {
1149              int id = GetInstrumentId(Instr);              int id = GetInstrumentId(Instr);
1150              if(id == -1) throw Exception("Unknown DB instrument: " + Instr);              if(id == -1) throw Exception("Unknown DB instrument: " + toEscapedPath(Instr));
1151    
1152              std::stringstream sql;              std::stringstream sql;
1153              sql << "UPDATE instruments SET description=?,modified=CURRENT_TIMESTAMP ";              sql << "UPDATE instruments SET description=?,modified=CURRENT_TIMESTAMP ";
# Line 1394  namespace LinuxSampler { Line 1162  namespace LinuxSampler {
1162          FireInstrumentInfoChanged(Instr);          FireInstrumentInfoChanged(Instr);
1163      }      }
1164    
1165      void InstrumentsDb::AddInstrumentsFromFile(String DbDir, String File, int Index) {      void InstrumentsDb::AddInstrumentsFromFile(String DbDir, String File, int Index, ScanProgress* pProgress) {
1166          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));
1167                    
1168          if(File.length() < 4) return;          if(File.length() < 4) return;
1169                    
1170          try {          try {
1171              if(!strcasecmp(".gig", File.substr(File.length() - 4).c_str())) {              if(!strcasecmp(".gig", File.substr(File.length() - 4).c_str())) {
1172                  AddGigInstruments(DbDir, File, Index);                  if (pProgress != NULL) {
1173                        pProgress->SetStatus(0);
1174                        pProgress->CurrentFile = File;
1175                    }
1176    
1177                    AddGigInstruments(DbDir, File, Index, pProgress);
1178    
1179                    if (pProgress != NULL) {
1180                        pProgress->SetScannedFileCount(pProgress->GetScannedFileCount() + 1);
1181                    }
1182              }              }
1183          } catch(Exception e) {          } catch(Exception e) {
1184              std::cerr << e.Message() << std::endl;              e.PrintMessage();
1185          }          }
1186      }      }
1187    
1188      void InstrumentsDb::AddGigInstruments(String DbDir, String File, int Index) {      void InstrumentsDb::AddGigInstruments(String DbDir, String FilePath, int Index, ScanProgress* pProgress) {
1189          dmsg(2,("InstrumentsDb: AddGigInstruments(DbDir=%s,File=%s,Index=%d)\n", DbDir.c_str(), File.c_str(), Index));          dmsg(2,("InstrumentsDb: AddGigInstruments(DbDir=%s,FilePath=%s,Index=%d)\n", DbDir.c_str(), FilePath.c_str(), Index));
1190          int dirId = GetDirectoryId(DbDir);          int dirId = GetDirectoryId(DbDir);
1191          if (dirId == -1) throw Exception("Invalid DB directory: " + DbDir);          if (dirId == -1) throw Exception("Invalid DB directory: " + toEscapedPath(DbDir));
1192    
1193          struct stat statBuf;          File f = File(FilePath);
1194          int res = stat(File.c_str(), &statBuf);          if (!f.Exist()) {
         if (res) {  
1195              std::stringstream ss;              std::stringstream ss;
1196              ss << "Fail to stat `" << File << "`: " << strerror(errno);              ss << "Fail to stat `" << FilePath << "`: " << f.GetErrorMsg();
1197              throw Exception(ss.str());              throw Exception(ss.str());
1198          }          }
1199    
1200          if (!S_ISREG(statBuf.st_mode)) {          if (!f.IsFile()) {
1201              std::stringstream ss;              std::stringstream ss;
1202              ss << "`" << File << "` is not a regular file";              ss << "`" << FilePath << "` is not a regular file";
1203              throw Exception(ss.str());              throw Exception(ss.str());
1204          }          }
1205    
1206            bool unlocked = false;
1207          RIFF::File* riff = NULL;          RIFF::File* riff = NULL;
1208          gig::File* gig = NULL;          gig::File* gig = NULL;
1209          try {          try {
1210              riff = new RIFF::File(File);              riff = new RIFF::File(FilePath);
1211              gig::File* gig = new gig::File(riff);              gig::File* gig = new gig::File(riff);
1212                            gig->SetAutoLoad(false); // avoid time consuming samples scanning
1213    
1214              std::stringstream sql;              std::stringstream sql;
1215              sql << "INSERT INTO instruments (dir_id,instr_name,instr_file,";              sql << "INSERT INTO instruments (dir_id,instr_name,instr_file,";
1216              sql << "instr_nr,format_family,format_version,instr_size,";              sql << "instr_nr,format_family,format_version,instr_size,";
1217              sql << "description,is_drum,product,artists,keywords) VALUES (";              sql << "description,is_drum,product,artists,keywords) VALUES (";
1218              sql << dirId << ",?,?,?,'GIG',?," << statBuf.st_size << ",?,?,?,?,?)";              sql << dirId << ",?,?,?,'GIG',?," << f.GetSize() << ",?,?,?,?,?)";
1219    
1220              sqlite3_stmt* pStmt = NULL;              sqlite3_stmt* pStmt = NULL;
1221    
# Line 1446  namespace LinuxSampler { Line 1224  namespace LinuxSampler {
1224                  throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));                  throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1225              }              }
1226    
1227              BindTextParam(pStmt, 2, File);              String s = FilePath;
1228    
1229                #if WIN32
1230                for (int i = 0; i < s.length(); i++) {
1231                    if (s[i] == '\\') s[i] = '/';
1232                }
1233                #endif
1234    
1235                s = toEscapedFsPath(s);
1236                BindTextParam(pStmt, 2, s);
1237              String ver = "";              String ver = "";
1238              if (gig->pVersion != NULL) ver = ToString(gig->pVersion->major);              if (gig->pVersion != NULL) ver = ToString(gig->pVersion->major);
1239              BindTextParam(pStmt, 4, ver);              BindTextParam(pStmt, 4, ver);
1240    
1241              if (Index == -1) {              if (Index == -1) {
1242                  int instrIndex = 0;                  int instrIndex = 0;
1243                    // Assume that it's locked and should be unlocked at this point
1244                    // to be able to use the database from another threads
1245                    if (!InTransaction) {
1246                        DbInstrumentsMutex.Unlock();
1247                        unlocked = true;
1248                    } else {
1249                        std::cerr << "Shouldn't be in transaction when adding instruments." << std::endl;
1250                    }
1251    
1252                    if (pProgress != NULL) gig->GetInstrument(0, &(pProgress->GigFileProgress)); // TODO: this workaround should be fixed
1253                  gig::Instrument* pInstrument = gig->GetFirstInstrument();                  gig::Instrument* pInstrument = gig->GetFirstInstrument();
1254    
1255                    if (!InTransaction) DbInstrumentsMutex.Lock();
1256                  while (pInstrument) {                  while (pInstrument) {
1257                      BindTextParam(pStmt, 7, gig->pInfo->Product);                      BindTextParam(pStmt, 7, gig->pInfo->Product);
1258                      BindTextParam(pStmt, 8, gig->pInfo->Artists);                      BindTextParam(pStmt, 8, gig->pInfo->Artists);
1259                      BindTextParam(pStmt, 9, gig->pInfo->Keywords);                      BindTextParam(pStmt, 9, gig->pInfo->Keywords);
1260                      AddGigInstrument(pStmt, DbDir, dirId, File, pInstrument, instrIndex);                      AddGigInstrument(pStmt, DbDir, dirId, FilePath, pInstrument, instrIndex);
1261    
1262                      instrIndex++;                      instrIndex++;
1263                      pInstrument = gig->GetNextInstrument();                      pInstrument = gig->GetNextInstrument();
1264                  }                  }
1265              } else {              } else {
1266                  gig::Instrument* pInstrument = gig->GetInstrument(Index);                  gig::Instrument* pInstrument;
1267                    if (pProgress == NULL) pInstrument = gig->GetInstrument(Index);
1268                    else pInstrument = gig->GetInstrument(Index, &(pProgress->GigFileProgress));
1269                  if (pInstrument != NULL) {                  if (pInstrument != NULL) {
1270                      BindTextParam(pStmt, 7, gig->pInfo->Product);                      BindTextParam(pStmt, 7, gig->pInfo->Product);
1271                      BindTextParam(pStmt, 8, gig->pInfo->Artists);                      BindTextParam(pStmt, 8, gig->pInfo->Artists);
1272                      BindTextParam(pStmt, 9, gig->pInfo->Keywords);                      BindTextParam(pStmt, 9, gig->pInfo->Keywords);
1273                      AddGigInstrument(pStmt, DbDir, dirId, File, pInstrument, Index);                      AddGigInstrument(pStmt, DbDir, dirId, FilePath, pInstrument, Index);
1274                  }                  }
1275              }              }
1276    
# Line 1479  namespace LinuxSampler { Line 1280  namespace LinuxSampler {
1280          } catch (RIFF::Exception e) {          } catch (RIFF::Exception e) {
1281              if (gig != NULL) delete gig;              if (gig != NULL) delete gig;
1282              if (riff != NULL) delete riff;              if (riff != NULL) delete riff;
1283                if (unlocked) DbInstrumentsMutex.Lock();
1284              std::stringstream ss;              std::stringstream ss;
1285              ss << "Failed to scan `" << File << "`: " << e.Message;              ss << "Failed to scan `" << FilePath << "`: " << e.Message;
1286                            
1287              throw Exception(ss.str());              throw Exception(ss.str());
1288          } catch (Exception e) {          } catch (Exception e) {
1289              if (gig != NULL) delete gig;              if (gig != NULL) delete gig;
1290              if (riff != NULL) delete riff;              if (riff != NULL) delete riff;
1291                if (unlocked) DbInstrumentsMutex.Lock();
1292              throw e;              throw e;
1293          } catch (...) {          } catch (...) {
1294              if (gig != NULL) delete gig;              if (gig != NULL) delete gig;
1295              if (riff != NULL) delete riff;              if (riff != NULL) delete riff;
1296              throw Exception("Failed to scan `" + File + "`");              if (unlocked) DbInstrumentsMutex.Lock();
1297                throw Exception("Failed to scan `" + FilePath + "`");
1298          }          }
1299      }      }
1300    
1301      void InstrumentsDb::AddGigInstrument(sqlite3_stmt* pStmt, String DbDir, int DirId, String File, gig::Instrument* pInstrument, int Index) {      void InstrumentsDb::AddGigInstrument(sqlite3_stmt* pStmt, String DbDir, int DirId, String File, gig::Instrument* pInstrument, int Index) {
1302            dmsg(2,("InstrumentsDb: AddGigInstrument(DbDir=%s,DirId=%d,File=%s,Index=%d)\n", DbDir.c_str(), DirId, File.c_str(), Index));
1303          String name = pInstrument->pInfo->Name;          String name = pInstrument->pInfo->Name;
1304          if (name == "") return;          if (name == "") return;
1305          name = GetUniqueInstrumentName(DirId, name);          name = GetUniqueName(DirId, name);
1306                    
1307          std::stringstream sql2;          std::stringstream sql2;
1308          sql2 << "SELECT COUNT(*) FROM instruments WHERE instr_file=? AND ";          sql2 << "SELECT COUNT(*) FROM instruments WHERE instr_file=? AND ";
1309          sql2 << "instr_nr=" << Index;          sql2 << "instr_nr=" << Index;
1310          if (ExecSqlInt(sql2.str(), File) > 0) return;          String s = toEscapedFsPath(File);
1311            if (ExecSqlInt(sql2.str(), s) > 0) return;
1312    
1313          BindTextParam(pStmt, 1, name);          BindTextParam(pStmt, 1, name);
1314          BindIntParam(pStmt, 3, Index);          BindIntParam(pStmt, 3, Index);
# Line 1531  namespace LinuxSampler { Line 1337  namespace LinuxSampler {
1337          FireInstrumentCountChanged(DbDir);          FireInstrumentCountChanged(DbDir);
1338      }      }
1339    
1340      void InstrumentsDb::DirectoryTreeWalk(String Path, DirectoryHandler* pHandler) {      void InstrumentsDb::DirectoryTreeWalk(String AbstractPath, DirectoryHandler* pHandler) {
1341          int DirId = GetDirectoryId(Path);          int DirId = GetDirectoryId(AbstractPath);
1342          if(DirId == -1) throw Exception("Unknown DB directory: " + Path);          if(DirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(AbstractPath));
1343          DirectoryTreeWalk(pHandler, Path, DirId, 0);          DirectoryTreeWalk(pHandler, AbstractPath, DirId, 0);
1344      }      }
1345    
1346      void InstrumentsDb::DirectoryTreeWalk(DirectoryHandler* pHandler, String Path, int DirId, int Level) {      void InstrumentsDb::DirectoryTreeWalk(DirectoryHandler* pHandler, String AbstractPath, int DirId, int Level) {
1347          if(Level == 1000) throw Exception("Possible infinite loop detected");          if(Level == 1000) throw Exception("Possible infinite loop detected");
1348          pHandler->ProcessDirectory(Path, DirId);          pHandler->ProcessDirectory(AbstractPath, DirId);
1349                    
1350          String s;          String s;
1351          StringListPtr pDirs = GetDirectories(DirId);          StringListPtr pDirs = GetDirectories(DirId);
1352          for(int i = 0; i < pDirs->size(); i++) {          for(int i = 0; i < pDirs->size(); i++) {
1353              if (Path.length() == 1 && Path.at(0) == '/') s = "/" + pDirs->at(i);              if (AbstractPath.length() == 1 && AbstractPath.at(0) == '/') {
1354              else s = Path + "/" + pDirs->at(i);                  s = "/" + pDirs->at(i);
1355                } else {
1356                    s = AbstractPath + "/" + pDirs->at(i);
1357                }
1358              DirectoryTreeWalk(pHandler, s, GetDirectoryId(DirId, pDirs->at(i)), Level + 1);              DirectoryTreeWalk(pHandler, s, GetDirectoryId(DirId, pDirs->at(i)), Level + 1);
1359          }          }
1360      }      }
# Line 1557  namespace LinuxSampler { Line 1366  namespace LinuxSampler {
1366          BeginTransaction();          BeginTransaction();
1367          try {          try {
1368              int DirId = GetDirectoryId(Dir);              int DirId = GetDirectoryId(Dir);
1369              if(DirId == -1) throw Exception("Unknown DB directory: " + Dir);              if(DirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
1370    
1371              if (Recursive) DirectoryTreeWalk(Dir, &directoryFinder);              if (Recursive) DirectoryTreeWalk(Dir, &directoryFinder);
1372              else directoryFinder.ProcessDirectory(Dir, DirId);              else directoryFinder.ProcessDirectory(Dir, DirId);
# Line 1577  namespace LinuxSampler { Line 1386  namespace LinuxSampler {
1386          BeginTransaction();          BeginTransaction();
1387          try {          try {
1388              int DirId = GetDirectoryId(Dir);              int DirId = GetDirectoryId(Dir);
1389              if(DirId == -1) throw Exception("Unknown DB directory: " + Dir);              if(DirId == -1) throw Exception("Unknown DB directory: " + toEscapedPath(Dir));
1390    
1391              if (Recursive) DirectoryTreeWalk(Dir, &instrumentFinder);              if (Recursive) DirectoryTreeWalk(Dir, &instrumentFinder);
1392              else instrumentFinder.ProcessDirectory(Dir, DirId);              else instrumentFinder.ProcessDirectory(Dir, DirId);
# Line 1589  namespace LinuxSampler { Line 1398  namespace LinuxSampler {
1398    
1399          return instrumentFinder.GetInstruments();          return instrumentFinder.GetInstruments();
1400      }      }
1401        
1402        StringListPtr InstrumentsDb::FindLostInstrumentFiles() {
1403            dmsg(2,("InstrumentsDb: FindLostInstrumentFiles()\n"));
1404    
1405            BeginTransaction();
1406            try {
1407                StringListPtr files = ExecSqlStringList("SELECT DISTINCT instr_file FROM instruments");
1408                StringListPtr result(new std::vector<String>);
1409                for (int i = 0; i < files->size(); i++) {
1410                    File f(toNonEscapedFsPath(files->at(i)));
1411                    if (!f.Exist()) result->push_back(files->at(i));
1412                }
1413                return result;
1414            } catch (Exception e) {
1415                EndTransaction();
1416                throw e;
1417            }
1418            EndTransaction();
1419        }
1420        
1421        void InstrumentsDb::SetInstrumentFilePath(String OldPath, String NewPath) {
1422            if (OldPath == NewPath) return;
1423            StringListPtr instrs;
1424            BeginTransaction();
1425            try {
1426                std::vector<String> params(2);
1427                params[0] = toEscapedFsPath(NewPath);
1428                params[1] = toEscapedFsPath(OldPath);
1429                instrs = GetInstrumentsByFile(OldPath);
1430                ExecSql("UPDATE instruments SET instr_file=? WHERE instr_file=?", params);
1431            } catch (Exception e) {
1432                EndTransaction();
1433                throw e;
1434            }
1435            EndTransaction();
1436            
1437            for (int i = 0; i < instrs->size(); i++) {
1438                FireInstrumentInfoChanged(instrs->at(i));
1439            }
1440        }
1441    
1442      void InstrumentsDb::BeginTransaction() {      void InstrumentsDb::BeginTransaction() {
1443          dmsg(2,("InstrumentsDb: BeginTransaction(InTransaction=%d)\n", InTransaction));          dmsg(2,("InstrumentsDb: BeginTransaction(InTransaction=%d)\n", InTransaction));
# Line 1650  namespace LinuxSampler { Line 1499  namespace LinuxSampler {
1499    
1500      void InstrumentsDb::ExecSql(String Sql) {      void InstrumentsDb::ExecSql(String Sql) {
1501          dmsg(2,("InstrumentsDb: ExecSql(Sql=%s)\n", Sql.c_str()));          dmsg(2,("InstrumentsDb: ExecSql(Sql=%s)\n", Sql.c_str()));
1502          sqlite3_stmt *pStmt = NULL;          std::vector<String> Params;
1503                    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);  
1504      }      }
1505    
1506      void InstrumentsDb::ExecSql(String Sql, String Param) {      void InstrumentsDb::ExecSql(String Sql, String Param) {
1507          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()));
1508            std::vector<String> Params;
1509            Params.push_back(Param);
1510            ExecSql(Sql, Params);
1511        }
1512    
1513        void InstrumentsDb::ExecSql(String Sql, std::vector<String>& Params) {
1514            dmsg(2,("InstrumentsDb: ExecSql(Sql=%s,Params)\n", Sql.c_str()));
1515          sqlite3_stmt *pStmt = NULL;          sqlite3_stmt *pStmt = NULL;
1516                    
1517          int res = sqlite3_prepare(GetDb(), Sql.c_str(), -1, &pStmt, NULL);          int res = sqlite3_prepare(GetDb(), Sql.c_str(), -1, &pStmt, NULL);
# Line 1676  namespace LinuxSampler { Line 1520  namespace LinuxSampler {
1520              throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));              throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1521          }          }
1522    
1523          BindTextParam(pStmt, 1, Param);          for(int i = 0; i < Params.size(); i++) {
1524                BindTextParam(pStmt, i + 1, Params[i]);
1525            }
1526    
1527          res = sqlite3_step(pStmt);          res = sqlite3_step(pStmt);
1528          if (res != SQLITE_DONE) {          if (res != SQLITE_DONE) {
# Line 1758  namespace LinuxSampler { Line 1604  namespace LinuxSampler {
1604      }      }
1605    
1606      IntListPtr InstrumentsDb::ExecSqlIntList(String Sql) {      IntListPtr InstrumentsDb::ExecSqlIntList(String Sql) {
1607            dmsg(2,("InstrumentsDb: ExecSqlIntList(Sql=%s)\n", Sql.c_str()));
1608            std::vector<String> Params;
1609            return ExecSqlIntList(Sql, Params);
1610        }
1611    
1612        IntListPtr InstrumentsDb::ExecSqlIntList(String Sql, String Param) {
1613            dmsg(2,("InstrumentsDb: ExecSqlIntList(Sql=%s,Param=%s)\n", Sql.c_str(), Param.c_str()));
1614            std::vector<String> Params;
1615            Params.push_back(Param);
1616            return ExecSqlIntList(Sql, Params);
1617        }
1618    
1619        IntListPtr InstrumentsDb::ExecSqlIntList(String Sql, std::vector<String>& Params) {
1620            dmsg(2,("InstrumentsDb: ExecSqlIntList(Sql=%s)\n", Sql.c_str()));
1621          IntListPtr intList(new std::vector<int>);          IntListPtr intList(new std::vector<int>);
1622                    
1623          sqlite3_stmt *pStmt = NULL;          sqlite3_stmt *pStmt = NULL;
# Line 1767  namespace LinuxSampler { Line 1627  namespace LinuxSampler {
1627              throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));              throw Exception("DB error: " + ToString(sqlite3_errmsg(db)));
1628          }          }
1629                    
1630            for(int i = 0; i < Params.size(); i++) {
1631                BindTextParam(pStmt, i + 1, Params[i]);
1632            }
1633            
1634          res = sqlite3_step(pStmt);          res = sqlite3_step(pStmt);
1635          while(res == SQLITE_ROW) {          while(res == SQLITE_ROW) {
1636              intList->push_back(sqlite3_column_int(pStmt, 0));              intList->push_back(sqlite3_column_int(pStmt, 0));
# Line 1784  namespace LinuxSampler { Line 1648  namespace LinuxSampler {
1648      }      }
1649            
1650      StringListPtr InstrumentsDb::ExecSqlStringList(String Sql) {      StringListPtr InstrumentsDb::ExecSqlStringList(String Sql) {
1651            dmsg(2,("InstrumentsDb: ExecSqlStringList(Sql=%s)\n", Sql.c_str()));
1652          StringListPtr stringList(new std::vector<String>);          StringListPtr stringList(new std::vector<String>);
1653                    
1654          sqlite3_stmt *pStmt = NULL;          sqlite3_stmt *pStmt = NULL;
# Line 1827  namespace LinuxSampler { Line 1692  namespace LinuxSampler {
1692          }          }
1693      }      }
1694    
1695    #ifndef WIN32
1696      void InstrumentsDb::Regexp(sqlite3_context* pContext, int argc, sqlite3_value** ppValue) {      void InstrumentsDb::Regexp(sqlite3_context* pContext, int argc, sqlite3_value** ppValue) {
1697          if (argc != 2) return;          if (argc != 2) return;
1698    
# Line 1837  namespace LinuxSampler { Line 1703  namespace LinuxSampler {
1703              sqlite3_result_int(pContext, 1);              sqlite3_result_int(pContext, 1);
1704          }          }
1705      }      }
1706    #endif
1707    
1708      String InstrumentsDb::GetDirectoryPath(String File) {      String InstrumentsDb::GetDirectoryPath(String File) {
1709          if (File.empty()) return String("");          if (File.empty()) return String("");
# Line 1879  namespace LinuxSampler { Line 1746  namespace LinuxSampler {
1746          return Dir.substr(0, i);          return Dir.substr(0, i);
1747      }      }
1748    
1749        void InstrumentsDb::Format() {
1750            DbInstrumentsMutex.Lock();
1751            if (db != NULL) {
1752                sqlite3_close(db);
1753                db = NULL;
1754            }
1755    
1756            if (DbFile.empty()) DbFile = CONFIG_DEFAULT_INSTRUMENTS_DB_LOCATION;
1757            String bkp = DbFile + ".bkp";
1758            remove(bkp.c_str());
1759            if (rename(DbFile.c_str(), bkp.c_str()) && errno != ENOENT) {
1760                DbInstrumentsMutex.Unlock();
1761                throw Exception(String("Failed to backup database: ") + strerror(errno));
1762            }
1763            
1764            String f = DbFile;
1765            DbFile = "";
1766            try { CreateInstrumentsDb(f); }
1767            catch(Exception e) {
1768                DbInstrumentsMutex.Unlock();
1769                throw e;
1770            }
1771            DbInstrumentsMutex.Unlock();
1772            
1773            FireDirectoryCountChanged("/");
1774            FireInstrumentCountChanged("/");
1775        }
1776    
1777      void InstrumentsDb::CheckFileName(String File) {      void InstrumentsDb::CheckFileName(String File) {
1778          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);  
         }  
1779      }      }
1780    
1781      String InstrumentsDb::GetUniqueInstrumentName(int DirId, String Name) {      String InstrumentsDb::GetUniqueName(int DirId, String Name) {
1782          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()));
1783    
1784          if (GetInstrumentId(DirId, Name) == -1 && GetDirectoryId(DirId, Name) == -1) return Name;          if (GetInstrumentId(DirId, Name) == -1 && GetDirectoryId(DirId, Name) == -1) return Name;
# Line 1902  namespace LinuxSampler { Line 1794  namespace LinuxSampler {
1794          throw Exception("Unable to find an unique name: " + Name);          throw Exception("Unable to find an unique name: " + Name);
1795      }      }
1796            
1797        String InstrumentsDb::PrepareSubdirectory(String DbDir, String FsPath) {
1798            std::string dir = Path::getBaseName(FsPath);
1799            dir = toAbstractName(dir);
1800            if(dir.empty()) dir = "New Directory";
1801            dir = GetUniqueName(GetDirectoryId(DbDir), dir);
1802            dir = AppendNode(DbDir, dir);
1803            AddDirectory(dir);
1804            return dir;
1805        }
1806    
1807        String InstrumentsDb::AppendNode(String DbDir, String Node) {
1808            if(DbDir.length() == 1 && DbDir.at(0) == '/') return DbDir + Node;
1809            if(DbDir.at(DbDir.length() - 1) == '/') return DbDir + Node;
1810            return DbDir + "/" + Node;
1811        }
1812    
1813        String InstrumentsDb::toDbName(String AbstractName) {
1814            for (int i = 0; i < AbstractName.length(); i++) {
1815                if (AbstractName.at(i) == '\0') AbstractName.at(i) = '/';
1816            }
1817            return AbstractName;
1818        }
1819    
1820        String InstrumentsDb::toEscapedPath(String AbstractName) {
1821            for (int i = 0; i < AbstractName.length(); i++) {
1822                if (AbstractName.at(i) == '\0')      AbstractName.replace(i++, 1, "\\x2f");
1823                else if (AbstractName.at(i) == '\\') AbstractName.replace(i++, 1, "\\\\");
1824                else if (AbstractName.at(i) == '\'') AbstractName.replace(i++, 1, "\\'");
1825                else if (AbstractName.at(i) == '"')  AbstractName.replace(i++, 1, "\\\"");
1826                else if (AbstractName.at(i) == '\r') AbstractName.replace(i++, 1, "\\r");
1827                else if (AbstractName.at(i) == '\n') AbstractName.replace(i++, 1, "\\n");
1828            }
1829            return AbstractName;
1830        }
1831        
1832        String InstrumentsDb::toEscapedText(String text) {
1833            for (int i = 0; i < text.length(); i++) {
1834                if (text.at(i) == '\\')      text.replace(i++, 1, "\\\\");
1835                else if (text.at(i) == '\'') text.replace(i++, 1, "\\'");
1836                else if (text.at(i) == '"')  text.replace(i++, 1, "\\\"");
1837                else if (text.at(i) == '\r') text.replace(i++, 1, "\\r");
1838                else if (text.at(i) == '\n') text.replace(i++, 1, "\\n");
1839            }
1840            return text;
1841        }
1842        
1843        String InstrumentsDb::toNonEscapedText(String text) {
1844            String sb;
1845            for (int i = 0; i < text.length(); i++) {
1846                char c = text.at(i);
1847                            if(c == '\\') {
1848                                    if(i >= text.length()) {
1849                                            std::cerr << "Broken escape sequence!" << std::endl;
1850                                            break;
1851                                    }
1852                                    char c2 = text.at(++i);
1853                                    if(c2 == '\'')      sb.push_back('\'');
1854                                    else if(c2 == '"')  sb.push_back('"');
1855                                    else if(c2 == '\\') sb.push_back('\\');
1856                                    else if(c2 == 'r')  sb.push_back('\r');
1857                                    else if(c2 == 'n')  sb.push_back('\n');
1858                                    else std::cerr << "Unknown escape sequence \\" << c2 << std::endl;
1859                            } else {
1860                                    sb.push_back(c);
1861                            }
1862            }
1863            return sb;
1864        }
1865        
1866        String InstrumentsDb::toEscapedFsPath(String FsPath) {
1867            return toEscapedText(FsPath);
1868        }
1869        
1870        String InstrumentsDb::toNonEscapedFsPath(String FsPath) {
1871            return toNonEscapedText(FsPath);
1872        }
1873        
1874        String InstrumentsDb::toAbstractName(String DbName) {
1875            for (int i = 0; i < DbName.length(); i++) {
1876                if (DbName.at(i) == '/') DbName.at(i) = '\0';
1877            }
1878            return DbName;
1879        }
1880    
1881      void InstrumentsDb::FireDirectoryCountChanged(String Dir) {      void InstrumentsDb::FireDirectoryCountChanged(String Dir) {
1882          for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {          for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {
1883              llInstrumentsDbListeners.GetListener(i)->DirectoryCountChanged(Dir);              llInstrumentsDbListeners.GetListener(i)->DirectoryCountChanged(Dir);
1884          }          }
1885      }      }
1886        
1887      void InstrumentsDb::FireDirectoryInfoChanged(String Dir) {      void InstrumentsDb::FireDirectoryInfoChanged(String Dir) {
1888          for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {          for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {
1889              llInstrumentsDbListeners.GetListener(i)->DirectoryInfoChanged(Dir);              llInstrumentsDbListeners.GetListener(i)->DirectoryInfoChanged(Dir);
1890          }          }
1891      }      }
1892        
1893      void InstrumentsDb::FireDirectoryNameChanged(String Dir, String NewName) {      void InstrumentsDb::FireDirectoryNameChanged(String Dir, String NewName) {
1894          for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {          for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {
1895              llInstrumentsDbListeners.GetListener(i)->DirectoryNameChanged(Dir, NewName);              llInstrumentsDbListeners.GetListener(i)->DirectoryNameChanged(Dir, NewName);
1896          }          }
1897      }      }
1898        
1899      void InstrumentsDb::FireInstrumentCountChanged(String Dir) {      void InstrumentsDb::FireInstrumentCountChanged(String Dir) {
1900          for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {          for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {
1901              llInstrumentsDbListeners.GetListener(i)->InstrumentCountChanged(Dir);              llInstrumentsDbListeners.GetListener(i)->InstrumentCountChanged(Dir);
1902          }          }
1903      }      }
1904        
1905      void InstrumentsDb::FireInstrumentInfoChanged(String Instr) {      void InstrumentsDb::FireInstrumentInfoChanged(String Instr) {
1906          for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {          for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {
1907              llInstrumentsDbListeners.GetListener(i)->InstrumentInfoChanged(Instr);              llInstrumentsDbListeners.GetListener(i)->InstrumentInfoChanged(Instr);
1908          }          }
1909      }      }
1910        
1911      void InstrumentsDb::FireInstrumentNameChanged(String Instr, String NewName) {      void InstrumentsDb::FireInstrumentNameChanged(String Instr, String NewName) {
1912          for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {          for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {
1913              llInstrumentsDbListeners.GetListener(i)->InstrumentNameChanged(Instr, NewName);              llInstrumentsDbListeners.GetListener(i)->InstrumentNameChanged(Instr, NewName);
1914          }          }
1915      }      }
       
1916    
1917      String DirectoryScanner::DbDir;      void InstrumentsDb::FireJobStatusChanged(int JobId) {
1918      String DirectoryScanner::FsDir;          for (int i = 0; i < llInstrumentsDbListeners.GetListenerCount(); i++) {
1919      bool DirectoryScanner::Flat;              llInstrumentsDbListeners.GetListener(i)->JobStatusChanged(JobId);
   
     void DirectoryScanner::Scan(String DbDir, String FsDir, bool Flat) {  
         dmsg(2,("DirectoryScanner: Scan(DbDir=%s,FsDir=%s,Flat=%d)\n", DbDir.c_str(), FsDir.c_str(), Flat));  
         if (DbDir.empty() || FsDir.empty()) throw Exception("Directory expected");  
           
         struct stat statBuf;  
         int res = stat(FsDir.c_str(), &statBuf);  
         if (res) {  
             std::stringstream ss;  
             ss << "Fail to stat `" << FsDir << "`: " << strerror(errno);  
             throw Exception(ss.str());  
         }  
   
         if (!S_ISDIR(statBuf.st_mode)) {  
             throw Exception("Directory expected");  
         }  
           
         DirectoryScanner::DbDir = DbDir;  
         DirectoryScanner::FsDir = FsDir;  
         if (DbDir.at(DbDir.length() - 1) != '/') {  
             DirectoryScanner::DbDir.append("/");  
         }  
         if (FsDir.at(FsDir.length() - 1) != '/') {  
             DirectoryScanner::FsDir.append("/");  
1920          }          }
         DirectoryScanner::Flat = Flat;  
           
         ftw(FsDir.c_str(), FtwCallback, 10);  
1921      }      }
1922    
     int DirectoryScanner::FtwCallback(const char* fpath, const struct stat* sb, int typeflag) {  
         dmsg(2,("DirectoryScanner: FtwCallback(fpath=%s)\n", fpath));  
         if (typeflag != FTW_D) return 0;  
   
         String dir = DbDir;  
         if (!Flat) {  
             String subdir = fpath;  
             if(subdir.length() > FsDir.length()) {  
                 subdir = subdir.substr(FsDir.length());  
                 dir += subdir;  
             }  
         }  
           
         InstrumentsDb* db = InstrumentsDb::GetInstrumentsDb();  
         if (!db->DirectoryExist(dir)) db->AddDirectory(dir);  
   
         db->AddInstrumentsNonrecursive(dir, String(fpath));  
   
         return 0;  
     };  
   
1923  } // namespace LinuxSampler  } // namespace LinuxSampler
   
 #endif // HAVE_SQLITE3  

Legend:
Removed from v.1187  
changed lines
  Added in v.1912

  ViewVC Help
Powered by ViewVC