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