1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /***************************************************************************
3 * directory.cc
4 *
5 * Tue Apr 23 22:01:07 CEST 2013
6 * Copyright 2013 Jonas Suhr Christensen
7 * jsc@umbraculum.org
8 ****************************************************************************/
9
10 /*
11 * This file is part of DrumGizmo.
12 *
13 * DrumGizmo is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU Lesser General Public License as published by
15 * the Free Software Foundation; either version 3 of the License, or
16 * (at your option) any later version.
17 *
18 * DrumGizmo is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License for more details.
22 *
23 * You should have received a copy of the GNU Lesser General Public License
24 * along with DrumGizmo; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
26 */
27 #include "directory.h"
28
29 #include <dirent.h>
30 #include <stdio.h>
31 #include <string>
32 #include <algorithm>
33 #include <vector>
34 #include <string.h>
35 #include <unistd.h>
36
37 #include <platform.h>
38
39 #if DG_PLATFORM == DG_PLATFORM_WINDOWS
40 #include <direct.h>
41 #include <windows.h>
42 #endif
43
44 #include <hugin.hpp>
45
46 #define DRUMKIT_SUFFIX ".xml"
47
48 // http://en.wikipedia.org/wiki/Path_(computing)
49 #if DG_PLATFORM == DG_PLATFORM_WINDOWS
50 #define SEP "\\"
51 #else
52 #define SEP "/"
53 #endif
54
Directory(std::string path)55 Directory::Directory(std::string path)
56 {
57 setPath(path);
58 }
59
~Directory()60 Directory::~Directory()
61 {
62 }
63
seperator()64 std::string Directory::seperator()
65 {
66 return SEP;
67 }
68
setPath(std::string path)69 void Directory::setPath(std::string path)
70 {
71 //DEBUG(directory, "Setting path to '%s'\n", path.c_str());
72 this->_path = cleanPath(path);
73 refresh();
74 }
75
count()76 size_t Directory::count()
77 {
78 return _files.size();
79 }
80
refresh()81 void Directory::refresh()
82 {
83 _files = listFiles(_path, DIRECTORY_HIDDEN);
84 //_files = listFiles(_path);
85 }
86
cd(std::string dir)87 bool Directory::cd(std::string dir)
88 {
89 //TODO: Should this return true or false?
90 if(dir.empty() || dir == ".")
91 {
92 return true;
93 }
94
95 //DEBUG(directory, "Changing to '%s'\n", dir.c_str());
96 if(exists(_path + SEP + dir))
97 {
98 std::string path = _path + SEP + dir;
99 setPath(path);
100 refresh();
101 return true;
102 }
103 else
104 {
105 return false;
106 }
107 }
108
cdUp()109 bool Directory::cdUp()
110 {
111 return this->cd("..");
112 }
113
path()114 std::string Directory::path()
115 {
116 return cleanPath(_path);
117 }
118
entryList()119 Directory::EntryList Directory::entryList()
120 {
121 return _files;
122 }
123
124 #define MAX_FILE_LENGTH 1024
cwd()125 std::string Directory::cwd()
126 {
127 char path[MAX_FILE_LENGTH];
128 char* c = getcwd(path, MAX_FILE_LENGTH);
129
130 if(c)
131 {
132 return c;
133 }
134 else
135 {
136 return "";
137 }
138 }
139
cleanPath(std::string path)140 std::string Directory::cleanPath(std::string path)
141 {
142 //DEBUG(directory, "Cleaning path '%s'\n", path.c_str());
143 Directory::Path pathlst = parsePath(path);
144 return Directory::pathToStr(pathlst);
145 }
146
listFiles(std::string path,unsigned char filter)147 Directory::EntryList Directory::listFiles(std::string path, unsigned char filter)
148 {
149 DEBUG(directory, "Listing files in '%s'\n", path.c_str());
150
151 Directory::EntryList entries;
152 DIR *dir = opendir(path.c_str());
153 if(!dir)
154 {
155 //DEBUG(directory, "Couldn't open directory '%s\n", path.c_str());
156 return entries;
157 }
158
159 std::vector<std::string> directories;
160 std::vector<std::string> files;
161
162 struct dirent *entry;
163 while((entry = readdir(dir)) != nullptr)
164 {
165 std::string name = entry->d_name;
166 if(name == ".")
167 {
168 continue;
169 }
170
171 if(Directory::isRoot(path) && name == "..")
172 {
173 continue;
174 }
175
176 unsigned char entryinfo = 0;
177 if(isHidden(path + SEP + name))
178 {
179 entryinfo |= DIRECTORY_HIDDEN;
180 }
181
182 std::string entrypath = path;
183 entrypath += SEP;
184 entrypath += entry->d_name;
185 if(Directory::isDir(entrypath))
186 {
187 if(!(entryinfo && filter))
188 {
189 if(name == "..")
190 {
191 directories.push_back(entry->d_name);
192 }
193 else
194 {
195 directories.push_back(std::string(SEP) + entry->d_name);
196 }
197 }
198 }
199 else
200 {
201 int drumkit_suffix_length = strlen(DRUMKIT_SUFFIX);
202 if((int)name.size() < drumkit_suffix_length)
203 {
204 continue;
205 }
206
207 if(name.substr(name.length() - drumkit_suffix_length,
208 drumkit_suffix_length) != DRUMKIT_SUFFIX)
209 {
210 continue;
211 }
212
213
214 //if(!(entryinfo && filter)) {
215 files.push_back(entry->d_name);
216 //}
217 }
218 }
219
220 #if DG_PLATFORM == DG_PLATFORM_WINDOWS
221 //DEBUG(directory, "Root is %s\n", Directory::root(path).c_str());
222 //DEBUG(directory, "Current path %s is root? %d", path.c_str(),
223 // Directory::isRoot(path));
224 if(Directory::isRoot(path))
225 {
226 entries.push_back("..");
227 }
228 #endif
229
230 // sort
231 for(int x = 0; x < (int)directories.size(); x++)
232 {
233 for(int y = 0; y < (int)directories.size(); y++)
234 {
235 if(directories[x] < directories[y])
236 {
237 std::string tmp = directories[x];
238 directories[x] = directories[y];
239 directories[y] = tmp;
240 }
241 }
242 }
243
244 for(int x = 0; x < (int)files.size(); x++)
245 {
246 for(int y = 0; y < (int)files.size(); y++)
247 {
248 if(files[x] < files[y])
249 {
250 std::string tmp = files[x];
251 files[x] = files[y];
252 files[y] = tmp;
253 }
254 }
255 }
256
257
258 for(auto directory : directories)
259 {
260 entries.push_back(directory);
261 }
262
263 for(auto file : files)
264 {
265 entries.push_back(file);
266 }
267
268 closedir(dir);
269 return entries;
270 }
271
isRoot(std::string path)272 bool Directory::isRoot(std::string path)
273 {
274 #if DG_PLATFORM == DG_PLATFORM_WINDOWS
275 std::transform(path.begin(), path.end(), path.begin(), ::tolower);
276 std::string root_str = Directory::root(path);
277 std::transform(root_str.begin(), root_str.end(), root_str.begin(), ::tolower);
278
279 // TODO: This is not a correct root calculation, but works with partitions
280 if(path.size() == 2)
281 {
282 if(path == root_str)
283 {
284 return true;
285 }
286 else
287 {
288 return false;
289 }
290 }
291 else
292 {
293 if (path.size() == 3)
294 {
295 if(path == root_str + SEP)
296 {
297 return true;
298 }
299 return false;
300 }
301 else
302 {
303 return false;
304 }
305 }
306 #else
307 if(path == SEP)
308 {
309 return true;
310 }
311 else
312 {
313 return false;
314 }
315 #endif
316 }
317
root()318 std::string Directory::root()
319 {
320 return root(cwd());
321 }
322
root(std::string path)323 std::string Directory::root(std::string path)
324 {
325 #if DG_PLATFORM == DG_PLATFORM_WINDOWS
326 if(path.size() < 2)
327 {
328 return "c:"; // just something default when input is bad
329 }
330 else
331 {
332 return path.substr(0, 2);
333 }
334 #else
335 return SEP;
336 #endif
337 }
338
drives()339 Directory::DriveList Directory::drives()
340 {
341 Directory::DriveList drives;
342 #if DG_PLATFORM == DG_PLATFORM_WINDOWS
343 unsigned int d = GetLogicalDrives();
344 for(int i = 0; i < 32; ++i)
345 {
346 if(d & (1 << i))
347 {
348 drive_t drive;
349 char name[] = "x:";
350 name[0] = i + 'a';
351
352 drive.name = name;
353 drive.number = i;
354 drives.push_back(drive);
355 }
356 }
357 #endif
358 return drives;
359 }
360
isDir()361 bool Directory::isDir()
362 {
363 return isDir(_path);
364 }
365
isDir(std::string path)366 bool Directory::isDir(std::string path)
367 {
368 //DEBUG(directory, "Is '%s' a directory?\n", path.c_str());
369 struct stat st;
370 if(stat(path.c_str(), &st) == 0)
371 {
372 if((st.st_mode & S_IFDIR) != 0)
373 {
374 //DEBUG(directory, "\t...yes!\n");
375 return true;
376 }
377 }
378 //DEBUG(directory, "\t...no!\n");
379 return false;
380 }
381
fileExists(std::string filename)382 bool Directory::fileExists(std::string filename)
383 {
384 return !isDir(_path + SEP + filename);
385 }
386
exists(std::string path)387 bool Directory::exists(std::string path)
388 {
389 struct stat st;
390 if(stat(path.c_str(), &st) == 0)
391 {
392 return true;
393 }
394 else
395 {
396 return false;
397 }
398 }
399
isHidden(std::string path)400 bool Directory::isHidden(std::string path)
401 {
402 //DEBUG(directory, "Is '%s' hidden?\n", path.c_str());
403 #if DG_PLATFORM == DG_PLATFORM_WINDOWS
404 // We dont want to filter out '..' pointing to root of a partition
405 unsigned pos = path.find_last_of("/\\");
406 std::string entry = path.substr(pos+1);
407 if(entry == "..")
408 {
409 return false;
410 }
411
412 DWORD fattribs = GetFileAttributes(path.c_str());
413 if(fattribs & FILE_ATTRIBUTE_HIDDEN)
414 {
415 //DEBUG(directory, "\t...yes!\n");
416 return true;
417 }
418 else
419 {
420 if(fattribs & FILE_ATTRIBUTE_SYSTEM)
421 {
422 //DEBUG(directory, "\t...yes!\n");
423 return true;
424 }
425 else
426 {
427 //DEBUG(directory, "\t...no!\n");
428 return false;
429 }
430 }
431 #else
432 unsigned pos = path.find_last_of("/\\");
433 std::string entry = path.substr(pos+1);
434 if(entry.size() > 1 &&
435 entry.at(0) == '.' &&
436 entry.at(1) != '.')
437 {
438 //DEBUG(directory, "\t...yes!\n");
439 return true;
440 }
441 else
442 {
443 //DEBUG(directory, "\t...no!\n");
444 return false;
445 }
446 #endif
447 }
448
parsePath(std::string path_str)449 Directory::Path Directory::parsePath(std::string path_str)
450 {
451 //TODO: Handle "." input and propably other special cases
452
453 //DEBUG(directory, "Parsing path '%s'", path_str.c_str());
454 Directory::Path path;
455
456 std::string current_char;
457 std::string prev_char;
458 std::string dir;
459 for(size_t c = 0; c < path_str.size(); ++c)
460 {
461 current_char = path_str.at(c);
462
463 if(current_char == SEP)
464 {
465 if(prev_char == SEP)
466 {
467 dir.clear();
468 prev_char = current_char;
469 continue;
470 }
471 else
472 {
473 if(prev_char == ".")
474 {
475 prev_char = current_char;
476 continue;
477 }
478 }
479 if(!dir.empty())
480 {
481 path.push_back(dir);
482 }
483 dir.clear();
484 continue;
485 }
486 else
487 {
488 if(current_char == ".")
489 {
490 if(prev_char == ".")
491 {
492 dir.clear();
493 if(!path.empty())
494 {
495 path.pop_back();
496 }
497 continue;
498 }
499 }
500 }
501 dir += current_char;
502 prev_char = current_char;
503 }
504
505 if(!dir.empty())
506 {
507 path.push_back(dir);
508 }
509
510 return path;
511 }
512
pathToStr(Directory::Path & path)513 std::string Directory::pathToStr(Directory::Path& path)
514 {
515 std::string cleaned_path;
516 //DEBUG(directory, "Number of directories in path is %d\n", (int)path.size());
517
518 for(auto it = path.begin(); it != path.end(); ++it)
519 {
520 std::string dir = *it;
521 //DEBUG(directory, "\tDir '%s'\n", dir.c_str());
522 #if DG_PLATFORM == DG_PLATFORM_WINDOWS
523 if(it != path.begin())
524 {
525 cleaned_path += SEP;
526 }
527 cleaned_path += dir;
528 #else
529 cleaned_path += SEP + dir;
530 #endif
531 }
532
533 //DEBUG(directory, "Cleaned path '%s'\n", cleaned_path.c_str());
534
535 if(cleaned_path.empty())
536 {
537 cleaned_path = Directory::root();
538 #if DG_PLATFORM == DG_PLATFORM_WINDOWS
539 cleaned_path += SEP;
540 #endif
541 }
542
543 #if DG_PLATFORM == DG_PLATFORM_WINDOWS
544 if(cleaned_path.size() == 2)
545 {
546 cleaned_path += SEP;
547 }
548 #endif
549
550 return cleaned_path;
551 }
552
pathDirectory(std::string filepath)553 std::string Directory::pathDirectory(std::string filepath)
554 {
555 if(Directory::isDir(filepath))
556 {
557 return filepath;
558 }
559
560 Directory::Path path = parsePath(filepath);
561 if(path.size() > 0)
562 {
563 path.pop_back();
564 }
565
566 return Directory::pathToStr(path);
567 }
568