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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

1 /***************************************************************************
2 * *
3 * Copyright (C) 2007 Grigor Iliev *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the Free Software *
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, *
18 * MA 02110-1301 USA *
19 ***************************************************************************/
20
21 #include "InstrumentsDbUtilities.h"
22
23 #if HAVE_SQLITE3
24
25 #include <dirent.h>
26 #include <errno.h>
27 #include <ftw.h>
28
29 #include "../common/Exception.h"
30 #include "InstrumentsDb.h"
31
32 namespace LinuxSampler {
33
34 void DbInstrument::Copy(const DbInstrument& Instr) {
35 if (this == &Instr) return;
36
37 InstrFile = Instr.InstrFile;
38 InstrNr = Instr.InstrNr;
39 FormatFamily = Instr.FormatFamily;
40 FormatVersion = Instr.FormatVersion;
41 Size = Instr.Size;
42 Created = Instr.Created;
43 Modified = Instr.Modified;
44 Description = Instr.Description;
45 IsDrum = Instr.IsDrum;
46 Product = Instr.Product;
47 Artists = Instr.Artists;
48 Keywords = Instr.Keywords;
49 }
50
51
52 void DbDirectory::Copy(const DbDirectory& Dir) {
53 if (this == &Dir) return;
54
55 Created = Dir.Created;
56 Modified = Dir.Modified;
57 Description = Dir.Description;
58 }
59
60 SearchQuery::SearchQuery() {
61 MinSize = -1;
62 MaxSize = -1;
63 InstrType = BOTH;
64 }
65
66 void SearchQuery::SetFormatFamilies(String s) {
67 if (s.length() == 0) return;
68 int i = 0;
69 int j = s.find(',', 0);
70
71 while (j != std::string::npos) {
72 FormatFamilies.push_back(s.substr(i, j - i));
73 i = j + 1;
74 j = s.find(',', i);
75 }
76
77 if (i < s.length()) FormatFamilies.push_back(s.substr(i));
78 }
79
80 void SearchQuery::SetSize(String s) {
81 String s2 = GetMin(s);
82 if (s2.length() > 0) MinSize = atoll(s2.c_str());
83 else MinSize = -1;
84
85 s2 = GetMax(s);
86 if (s2.length() > 0) MaxSize = atoll(s2.c_str());
87 else MaxSize = -1;
88 }
89
90 void SearchQuery::SetCreated(String s) {
91 CreatedAfter = GetMin(s);
92 CreatedBefore = GetMax(s);
93 }
94
95 void SearchQuery::SetModified(String s) {
96 ModifiedAfter = GetMin(s);
97 ModifiedBefore = GetMax(s);
98 }
99
100 String SearchQuery::GetMin(String s) {
101 if (s.length() < 3) return "";
102 if (s.at(0) == '.' && s.at(1) == '.') return "";
103 int i = s.find("..");
104 if (i == std::string::npos) return "";
105 return s.substr(0, i);
106 }
107
108 String SearchQuery::GetMax(String s) {
109 if (s.length() < 3) return "";
110 if (s.find("..", s.length() - 2) != std::string::npos) return "";
111 int i = s.find("..");
112 if (i == std::string::npos) return "";
113 return s.substr(i + 2);
114 }
115
116 void ScanJob::Copy(const ScanJob& Job) {
117 if (this == &Job) return;
118
119 JobId = Job.JobId;
120 FilesTotal = Job.FilesTotal;
121 FilesScanned = Job.FilesScanned;
122 Scanning = Job.Scanning;
123 Status = Job.Status;
124 }
125
126 int JobList::AddJob(ScanJob Job) {
127 if (Counter + 1 < Counter) Counter = 0;
128 else Counter++;
129 Job.JobId = Counter;
130 Jobs.push_back(Job);
131 if (Jobs.size() > 3) {
132 std::vector<ScanJob>::iterator iter = Jobs.begin();
133 Jobs.erase(iter);
134 }
135 return Job.JobId;
136 }
137
138 ScanJob& JobList::GetJobById(int JobId) {
139 for (int i = 0; i < Jobs.size(); i++) {
140 if (Jobs.at(i).JobId == JobId) return Jobs.at(i);
141 }
142
143 throw Exception("Invalid job ID: " + ToString(JobId));
144 }
145
146 bool AbstractFinder::IsRegex(String Pattern) {
147 if(Pattern.find('?') != String::npos) return true;
148 if(Pattern.find('*') != String::npos) return true;
149 return false;
150 }
151
152 void AbstractFinder::AddSql(String Col, String Pattern, std::stringstream& Sql) {
153 if (Pattern.length() == 0) return;
154
155 if (IsRegex(Pattern)) {
156 Sql << " AND " << Col << " regexp ?";
157 Params.push_back(Pattern);
158 return;
159 }
160
161 String buf;
162 std::vector<String> tokens;
163 std::vector<String> tokens2;
164 std::stringstream ss(Pattern);
165 while (ss >> buf) tokens.push_back(buf);
166
167 if (tokens.size() == 0) {
168 Sql << " AND " << Col << " LIKE ?";
169 Params.push_back("%" + Pattern + "%");
170 return;
171 }
172
173 bool b = false;
174 for (int i = 0; i < tokens.size(); i++) {
175 Sql << (i == 0 ? " AND (" : "");
176
177 for (int j = 0; j < tokens.at(i).length(); j++) {
178 if (tokens.at(i).at(j) == '+') tokens.at(i).at(j) = ' ';
179 }
180
181 ss.clear();
182 ss.str("");
183 ss << tokens.at(i);
184
185 tokens2.clear();
186 while (ss >> buf) tokens2.push_back(buf);
187
188 if (b && tokens2.size() > 0) Sql << " OR ";
189 if (tokens2.size() > 1) Sql << "(";
190 for (int j = 0; j < tokens2.size(); j++) {
191 if (j != 0) Sql << " AND ";
192 Sql << Col << " LIKE ?";
193 Params.push_back("%" + tokens2.at(j) + "%");
194 b = true;
195 }
196 if (tokens2.size() > 1) Sql << ")";
197 }
198 if (!b) Sql << "0)";
199 else Sql << ")";
200 }
201
202 DirectoryFinder::DirectoryFinder(SearchQuery* pQuery) : pDirectories(new std::vector<String>) {
203 pStmt = NULL;
204 this->pQuery = pQuery;
205 std::stringstream sql;
206 sql << "SELECT dir_name from instr_dirs WHERE dir_id!=0 AND parent_dir_id=?";
207
208 if (pQuery->CreatedAfter.length() != 0) {
209 sql << " AND created > ?";
210 Params.push_back(pQuery->CreatedAfter);
211 }
212 if (pQuery->CreatedBefore.length() != 0) {
213 sql << " AND created < ?";
214 Params.push_back(pQuery->CreatedBefore);
215 }
216 if (pQuery->ModifiedAfter.length() != 0) {
217 sql << " AND modified > ?";
218 Params.push_back(pQuery->ModifiedAfter);
219 }
220 if (pQuery->ModifiedBefore.length() != 0) {
221 sql << " AND modified < ?";
222 Params.push_back(pQuery->ModifiedBefore);
223 }
224
225 AddSql("dir_name", pQuery->Name, sql);
226 AddSql("description", pQuery->Description, sql);
227 SqlQuery = sql.str();
228
229 InstrumentsDb* idb = InstrumentsDb::GetInstrumentsDb();
230
231 int res = sqlite3_prepare(idb->GetDb(), SqlQuery.c_str(), -1, &pStmt, NULL);
232 if (res != SQLITE_OK) {
233 throw Exception("DB error: " + ToString(sqlite3_errmsg(idb->GetDb())));
234 }
235
236 for(int i = 0; i < Params.size(); i++) {
237 idb->BindTextParam(pStmt, i + 2, Params.at(i));
238 }
239 }
240
241 DirectoryFinder::~DirectoryFinder() {
242 if (pStmt != NULL) sqlite3_finalize(pStmt);
243 }
244
245 StringListPtr DirectoryFinder::GetDirectories() {
246 return pDirectories;
247 }
248
249 void DirectoryFinder::ProcessDirectory(String Path, int DirId) {
250 InstrumentsDb* idb = InstrumentsDb::GetInstrumentsDb();
251 idb->BindIntParam(pStmt, 1, DirId);
252
253 String s = Path;
254 if(Path.compare("/") != 0) s += "/";
255 int res = sqlite3_step(pStmt);
256 while(res == SQLITE_ROW) {
257 pDirectories->push_back(s + idb->toAbstractName(ToString(sqlite3_column_text(pStmt, 0))));
258 res = sqlite3_step(pStmt);
259 }
260
261 if (res != SQLITE_DONE) {
262 sqlite3_finalize(pStmt);
263 throw Exception("DB error: " + ToString(sqlite3_errmsg(idb->GetDb())));
264 }
265
266 res = sqlite3_reset(pStmt);
267 if (res != SQLITE_OK) {
268 sqlite3_finalize(pStmt);
269 throw Exception("DB error: " + ToString(sqlite3_errmsg(idb->GetDb())));
270 }
271 }
272
273 InstrumentFinder::InstrumentFinder(SearchQuery* pQuery) : pInstruments(new std::vector<String>) {
274 pStmt = NULL;
275 this->pQuery = pQuery;
276 std::stringstream sql;
277 sql << "SELECT instr_name from instruments WHERE dir_id=?";
278
279 if (pQuery->CreatedAfter.length() != 0) {
280 sql << " AND created > ?";
281 Params.push_back(pQuery->CreatedAfter);
282 }
283 if (pQuery->CreatedBefore.length() != 0) {
284 sql << " AND created < ?";
285 Params.push_back(pQuery->CreatedBefore);
286 }
287 if (pQuery->ModifiedAfter.length() != 0) {
288 sql << " AND modified > ?";
289 Params.push_back(pQuery->ModifiedAfter);
290 }
291 if (pQuery->ModifiedBefore.length() != 0) {
292 sql << " AND modified < ?";
293 Params.push_back(pQuery->ModifiedBefore);
294 }
295 if (pQuery->MinSize != -1) sql << " AND instr_size > " << pQuery->MinSize;
296 if (pQuery->MaxSize != -1) sql << " AND instr_size < " << pQuery->MaxSize;
297
298 if (pQuery->InstrType == SearchQuery::CHROMATIC) sql << " AND is_drum = 0";
299 else if (pQuery->InstrType == SearchQuery::DRUM) sql << " AND is_drum != 0";
300
301 if (pQuery->FormatFamilies.size() > 0) {
302 sql << " AND (format_family=?";
303 Params.push_back(pQuery->FormatFamilies.at(0));
304 for (int i = 1; i < pQuery->FormatFamilies.size(); i++) {
305 sql << "OR format_family=?";
306 Params.push_back(pQuery->FormatFamilies.at(i));
307 }
308 sql << ")";
309 }
310
311 AddSql("instr_name", pQuery->Name, sql);
312 AddSql("description", pQuery->Description, sql);
313 AddSql("product", pQuery->Product, sql);
314 AddSql("artists", pQuery->Artists, sql);
315 AddSql("keywords", pQuery->Keywords, sql);
316 SqlQuery = sql.str();
317
318 InstrumentsDb* idb = InstrumentsDb::GetInstrumentsDb();
319
320 int res = sqlite3_prepare(idb->GetDb(), SqlQuery.c_str(), -1, &pStmt, NULL);
321 if (res != SQLITE_OK) {
322 throw Exception("DB error: " + ToString(sqlite3_errmsg(idb->GetDb())));
323 }
324
325 for(int i = 0; i < Params.size(); i++) {
326 idb->BindTextParam(pStmt, i + 2, Params.at(i));
327 }
328 }
329
330 InstrumentFinder::~InstrumentFinder() {
331 if (pStmt != NULL) sqlite3_finalize(pStmt);
332 }
333
334 void InstrumentFinder::ProcessDirectory(String Path, int DirId) {
335 InstrumentsDb* idb = InstrumentsDb::GetInstrumentsDb();
336 idb->BindIntParam(pStmt, 1, DirId);
337
338 String s = Path;
339 if(Path.compare("/") != 0) s += "/";
340 int res = sqlite3_step(pStmt);
341 while(res == SQLITE_ROW) {
342 pInstruments->push_back(s + idb->toAbstractName(ToString(sqlite3_column_text(pStmt, 0))));
343 res = sqlite3_step(pStmt);
344 }
345
346 if (res != SQLITE_DONE) {
347 sqlite3_finalize(pStmt);
348 throw Exception("DB error: " + ToString(sqlite3_errmsg(idb->GetDb())));
349 }
350
351 res = sqlite3_reset(pStmt);
352 if (res != SQLITE_OK) {
353 sqlite3_finalize(pStmt);
354 throw Exception("DB error: " + ToString(sqlite3_errmsg(idb->GetDb())));
355 }
356 }
357
358 StringListPtr InstrumentFinder::GetInstruments() {
359 return pInstruments;
360 }
361
362 void DirectoryCounter::ProcessDirectory(String Path, int DirId) {
363 count += InstrumentsDb::GetInstrumentsDb()->GetDirectoryCount(DirId);
364 }
365
366 void InstrumentCounter::ProcessDirectory(String Path, int DirId) {
367 count += InstrumentsDb::GetInstrumentsDb()->GetInstrumentCount(DirId);
368 }
369
370 DirectoryCopier::DirectoryCopier(String SrcParentDir, String DestDir) {
371 this->SrcParentDir = SrcParentDir;
372 this->DestDir = DestDir;
373
374 if (DestDir.at(DestDir.length() - 1) != '/') {
375 this->DestDir.append("/");
376 }
377 if (SrcParentDir.at(SrcParentDir.length() - 1) != '/') {
378 this->SrcParentDir.append("/");
379 }
380 }
381
382 void DirectoryCopier::ProcessDirectory(String Path, int DirId) {
383 InstrumentsDb* db = InstrumentsDb::GetInstrumentsDb();
384
385 String dir = DestDir;
386 String subdir = Path;
387 if(subdir.length() > SrcParentDir.length()) {
388 subdir = subdir.substr(SrcParentDir.length());
389 dir += subdir;
390 db->AddDirectory(dir);
391 }
392
393 int dstDirId = db->GetDirectoryId(dir);
394 if(dstDirId == -1) {
395 throw Exception("Unkown DB directory: " + InstrumentsDb::toEscapedPath(dir));
396 }
397 IntListPtr ids = db->GetInstrumentIDs(DirId);
398 for (int i = 0; i < ids->size(); i++) {
399 String name = db->GetInstrumentName(ids->at(i));
400 db->CopyInstrument(ids->at(i), name, dstDirId, dir);
401 }
402 }
403
404 ScanProgress::ScanProgress() {
405 TotalFileCount = ScannedFileCount = Status = 0;
406 CurrentFile = "";
407 GigFileProgress.custom = this;
408 GigFileProgress.callback = GigFileProgressCallback;
409 }
410
411 void ScanProgress::StatusChanged() {
412 InstrumentsDb* db = InstrumentsDb::GetInstrumentsDb();
413 db->Jobs.GetJobById(JobId).FilesTotal = GetTotalFileCount();
414 db->Jobs.GetJobById(JobId).FilesScanned = GetScannedFileCount();
415 db->Jobs.GetJobById(JobId).Scanning = CurrentFile;
416 db->Jobs.GetJobById(JobId).Status = GetStatus();
417
418 InstrumentsDb::GetInstrumentsDb()->FireJobStatusChanged(JobId);
419 }
420
421 int ScanProgress::GetTotalFileCount() {
422 return TotalFileCount;
423 }
424
425 void ScanProgress::SetTotalFileCount(int Count) {
426 if (TotalFileCount == Count) return;
427 TotalFileCount = Count;
428 StatusChanged();
429 }
430
431 int ScanProgress::GetScannedFileCount() {
432 return ScannedFileCount;
433 }
434
435 void ScanProgress::SetScannedFileCount(int Count) {
436 if (ScannedFileCount == Count) return;
437 ScannedFileCount = Count;
438 if (Count > TotalFileCount) TotalFileCount = Count;
439 StatusChanged();
440 }
441
442 int ScanProgress::GetStatus() {
443 return Status;
444 }
445
446 void ScanProgress::SetStatus(int Status) {
447 if (this->Status == Status) return;
448 if (Status < 0) this->Status = 0;
449 else if (Status > 100) this->Status = 100;
450 else this->Status = Status;
451 StatusChanged();
452 }
453
454 void ScanProgress::SetErrorStatus(int Err) {
455 if (Err > 0) Err *= -1;
456 Status = Err;
457 StatusChanged();
458 }
459
460 void ScanProgress::GigFileProgressCallback(gig::progress_t* pProgress) {
461 if (pProgress == NULL) return;
462 ScanProgress* sp = static_cast<ScanProgress*> (pProgress->custom);
463
464 sp->SetStatus((int)(pProgress->factor * 100));
465 }
466
467 AddInstrumentsJob::AddInstrumentsJob(int JobId, ScanMode Mode, String DbDir, String FsDir) {
468 this->JobId = JobId;
469 Progress.JobId = JobId;
470 this->Mode = Mode;
471 this->DbDir = DbDir;
472 this->FsDir = FsDir;
473 }
474
475 void AddInstrumentsJob::Run() {
476 try {
477 InstrumentsDb* db = InstrumentsDb::GetInstrumentsDb();
478
479 switch (Mode) {
480 case NON_RECURSIVE:
481 Progress.SetTotalFileCount(GetFileCount());
482 db->AddInstrumentsNonrecursive(DbDir, FsDir, &Progress);
483 break;
484 case RECURSIVE:
485 db->AddInstrumentsRecursive(DbDir, FsDir, false, &Progress);
486 break;
487 case FLAT:
488 db->AddInstrumentsRecursive(DbDir, FsDir, true, &Progress);
489 break;
490 default:
491 throw Exception("Unknown scan mode");
492 }
493
494 // Just to be sure that the frontends will be notified about the job completion
495 if (Progress.GetTotalFileCount() != Progress.GetScannedFileCount()) {
496 Progress.SetTotalFileCount(Progress.GetScannedFileCount());
497 }
498 if (Progress.GetStatus() != 100) Progress.SetStatus(100);
499 } catch(Exception e) {
500 Progress.SetErrorStatus(-1);
501 throw e;
502 }
503 }
504
505 int AddInstrumentsJob::GetFileCount() {
506 int count = 0;
507
508 DIR* pDir = opendir(FsDir.c_str());
509 if (pDir == NULL) {
510 std::stringstream ss;
511 ss << "The scanning of directory `" << FsDir << "` failed: ";
512 ss << strerror(errno);
513 std::cerr << ss.str();
514 return 0;
515 }
516
517 struct dirent* pEnt = readdir(pDir);
518 while (pEnt != NULL) {
519 if (pEnt->d_type != DT_REG) {
520 pEnt = readdir(pDir);
521 continue;
522 }
523
524 String s(pEnt->d_name);
525 if(s.length() < 4) {
526 pEnt = readdir(pDir);
527 continue;
528 }
529 if(!strcasecmp(".gig", s.substr(s.length() - 4).c_str())) count++;
530
531 pEnt = readdir(pDir);
532 }
533
534 if (closedir(pDir)) {
535 std::stringstream ss;
536 ss << "Failed to close directory `" << FsDir << "`: ";
537 ss << strerror(errno);
538 std::cerr << ss.str();
539 }
540
541 return count;
542 }
543
544 AddInstrumentsFromFileJob::AddInstrumentsFromFileJob(int JobId, String DbDir, String FilePath, int Index) {
545 this->JobId = JobId;
546 Progress.JobId = JobId;
547 Progress.SetTotalFileCount(1);
548
549 this->DbDir = DbDir;
550 this->FilePath = FilePath;
551 this->Index = Index;
552 }
553
554 void AddInstrumentsFromFileJob::Run() {
555 try {
556 InstrumentsDb::GetInstrumentsDb()->AddInstruments(DbDir, FilePath, Index, &Progress);
557
558 // Just to be sure that the frontends will be notified about the job completion
559 if (Progress.GetTotalFileCount() != Progress.GetScannedFileCount()) {
560 Progress.SetTotalFileCount(Progress.GetScannedFileCount());
561 }
562 if (Progress.GetStatus() != 100) Progress.SetStatus(100);
563 } catch(Exception e) {
564 Progress.SetErrorStatus(-1);
565 throw e;
566 }
567 }
568
569
570 String DirectoryScanner::DbDir;
571 String DirectoryScanner::FsDir;
572 bool DirectoryScanner::Flat;
573 ScanProgress* DirectoryScanner::pProgress;
574
575 void DirectoryScanner::Scan(String DbDir, String FsDir, bool Flat, ScanProgress* pProgress) {
576 dmsg(2,("DirectoryScanner: Scan(DbDir=%s,FsDir=%s,Flat=%d)\n", DbDir.c_str(), FsDir.c_str(), Flat));
577 if (DbDir.empty() || FsDir.empty()) throw Exception("Directory expected");
578
579 struct stat statBuf;
580 int res = stat(FsDir.c_str(), &statBuf);
581 if (res) {
582 std::stringstream ss;
583 ss << "Fail to stat `" << FsDir << "`: " << strerror(errno);
584 throw Exception(ss.str());
585 }
586
587 if (!S_ISDIR(statBuf.st_mode)) {
588 throw Exception("Directory expected");
589 }
590
591 DirectoryScanner::DbDir = DbDir;
592 DirectoryScanner::FsDir = FsDir;
593 if (DbDir.at(DbDir.length() - 1) != '/') {
594 DirectoryScanner::DbDir.append("/");
595 }
596 if (FsDir.at(FsDir.length() - 1) != '/') {
597 DirectoryScanner::FsDir.append("/");
598 }
599 DirectoryScanner::Flat = Flat;
600 DirectoryScanner::pProgress = pProgress;
601
602 ftw(FsDir.c_str(), FtwCallback, 10);
603 }
604
605 int DirectoryScanner::FtwCallback(const char* fpath, const struct stat* sb, int typeflag) {
606 dmsg(2,("DirectoryScanner: FtwCallback(fpath=%s)\n", fpath));
607 if (typeflag != FTW_D) return 0;
608
609 String dir = DbDir;
610 if (!Flat) {
611 String subdir = fpath;
612 if(subdir.length() > FsDir.length()) {
613 subdir = subdir.substr(FsDir.length());
614 dir += subdir;
615 }
616 }
617
618 InstrumentsDb* db = InstrumentsDb::GetInstrumentsDb();
619
620 if (HasInstrumentFiles(String(fpath))) {
621 if (!db->DirectoryExist(dir)) db->AddDirectory(dir);
622 db->AddInstrumentsNonrecursive(dir, String(fpath), pProgress);
623 }
624
625 return 0;
626 };
627
628 bool DirectoryScanner::HasInstrumentFiles(String Dir) {
629 return InstrumentFileCounter::Count(Dir) > 0;
630 }
631
632 int InstrumentFileCounter::FileCount;
633
634 int InstrumentFileCounter::Count(String FsDir) {
635 dmsg(2,("InstrumentFileCounter: Count(FsDir=%s)\n", FsDir.c_str()));
636 if (FsDir.empty()) throw Exception("Directory expected");
637 FileCount = 0;
638
639 struct stat statBuf;
640 int res = stat(FsDir.c_str(), &statBuf);
641 if (res) {
642 std::stringstream ss;
643 ss << "Fail to stat `" << FsDir << "`: " << strerror(errno);
644 throw Exception(ss.str());
645 }
646
647 if (!S_ISDIR(statBuf.st_mode)) {
648 throw Exception("Directory expected");
649 }
650
651 ftw(FsDir.c_str(), FtwCallback, 10);
652 return FileCount;
653 }
654
655 int InstrumentFileCounter::FtwCallback(const char* fpath, const struct stat* sb, int typeflag) {
656 if (typeflag != FTW_F) return 0;
657 String s = fpath;
658 if(s.length() < 4) return 0;
659 if(!strcasecmp(".gig", s.substr(s.length() - 4).c_str())) FileCount++;
660
661 return 0;
662 };
663
664 } // namespace LinuxSampler
665
666 #endif // HAVE_SQLITE3

  ViewVC Help
Powered by ViewVC