]> Dogcows Code - chaz/yoink/blob - src/stlplus/portability/file_system.cpp
import stlplus 3.7
[chaz/yoink] / src / stlplus / portability / file_system.cpp
1 ////////////////////////////////////////////////////////////////////////////////
2
3 // Author: Andy Rushton
4 // Copyright: (c) Southampton University 1999-2004
5 // (c) Andy Rushton 2004 onwards
6 // License: BSD License, see ../docs/license.html
7
8 // This is a portable interface to the file system.
9
10 // The idea is that you write all file system access code using these functions,
11 // which are ported to all platforms that we are interested in. Therefore your
12 // code is inherently portable.
13
14 // Native Windows version: switched on by macro _WIN32 which is defined by VC++/Borland/Mingw compilers
15 // Unix/Gnu version: default variant, no compiler directives are required but _WIN32 must be absent
16 // Cygwin/Gnu version: as Unix version but with additional support for Windows drive letters
17
18 ////////////////////////////////////////////////////////////////////////////////
19 #include "file_system.hpp"
20 #include "wildcard.hpp"
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <time.h>
24 #include <algorithm>
25 #include <ctype.h>
26
27 #ifdef MSWINDOWS
28 #include <windows.h>
29 #include <dos.h>
30 #include <direct.h>
31 #include <fcntl.h>
32 #include <io.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #else
36 #include <dirent.h>
37 #include <fcntl.h>
38 #include <sys/param.h>
39 #include <unistd.h>
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #endif
43
44 ////////////////////////////////////////////////////////////////////////////////
45
46 namespace stlplus
47 {
48
49 ////////////////////////////////////////////////////////////////////////////////
50 // definitions of separators
51
52 #ifdef MSWINDOWS
53 static const char* separator_set = "\\/";
54 static const char preferred_separator = '\\';
55 #else
56 static const char* separator_set = "/";
57 static const char preferred_separator = '/';
58 #endif
59
60 static bool is_separator (char ch)
61 {
62 for (int i = 0; separator_set[i]; i++)
63 {
64 if (separator_set[i] == ch)
65 return true;
66 }
67 return false;
68 }
69
70 ////////////////////////////////////////////////////////////////////////////////
71 // implement string comparison of paths - Unix is case-sensitive, Windoze is case-insensitive
72
73 #ifdef MSWINDOWS
74
75 static std::string lowercase(const std::string& val)
76 {
77 std::string text = val;
78 for (unsigned i = 0; i < text.size(); i++)
79 text[i] = tolower(text[i]);
80 return text;
81 }
82
83 #endif
84
85 bool path_compare(const std::string& l, const std::string& r)
86 {
87 #ifdef MSWINDOWS
88 return lowercase(l) == lowercase(r);
89 #else
90 return l == r;
91 #endif
92 }
93
94 ////////////////////////////////////////////////////////////////////////////////
95 // Internal data structure used to hold the different parts of a filespec
96
97 class file_specification
98 {
99 private:
100 bool m_relative; // true = relative, false = absolute
101 std::string m_drive; // drive - drive letter (e.g. "c:") or the path for an UNC (e.g. "\\somewhere")
102 // empty if not known or on Unix
103 std::vector<std::string> m_path; // the subdirectory path to follow from the drive
104 std::string m_filename; // the filename
105 public:
106 file_specification(void) : m_relative(false) {}
107 ~file_specification(void) {}
108
109 bool initialise_folder(const std::string& spec);
110 bool initialise_file(const std::string& spec);
111 bool simplify(void);
112 bool make_absolute(const std::string& root = folder_current_full());
113 bool make_absolute(const file_specification& root);
114 bool make_relative(const std::string& root = folder_current_full());
115 bool make_relative(const file_specification& root);
116 bool relative(void) const {return m_relative;}
117 bool absolute(void) const {return !relative();}
118 void set_relative(void) {m_relative = true;}
119 void set_absolute(void) {m_relative = false;}
120
121 const std::string& drive(void) const {return m_drive;}
122 std::string& drive(void) {return m_drive;}
123 void set_drive(const std::string& drive) {m_drive = drive;}
124
125 const std::vector<std::string>& path(void) const {return m_path;}
126 std::vector<std::string>& path(void) {return m_path;}
127 void set_path(const std::vector<std::string>& path) {m_path = path;}
128
129 void add_subpath(const std::string& subpath) {m_path.push_back(subpath);}
130 unsigned subpath_size(void) const {return m_path.size();}
131 const std::string& subpath_element(unsigned i) const {return m_path[i];}
132 void subpath_erase(unsigned i) {m_path.erase(m_path.begin()+i);}
133
134 const std::string& file(void) const {return m_filename;}
135 std::string& file(void) {return m_filename;}
136 void set_file(const std::string& file) {m_filename = file;}
137
138 std::string image(void) const;
139 };
140
141 bool file_specification::initialise_folder(const std::string& folder_spec)
142 {
143 std::string spec = folder_spec;
144 m_relative = true;
145 m_drive.erase();
146 m_path.clear();
147 m_filename.erase();
148 unsigned i = 0;
149 #ifdef MSWINDOWS
150 // first split off the drive letter or UNC prefix on Windows
151 if (spec.size() >= 2 && isalpha(spec[0]) && spec[1] == ':')
152 {
153 // found a drive letter
154 i = 2;
155 m_drive = spec.substr(0, 2);
156 m_relative = false;
157 // if there is a drive but no path or a relative path, get the current
158 // path for this drive and prepend it to the path
159 if (i == spec.size() || !is_separator(spec[i]))
160 {
161 // getdcwd requires the drive number (1..26) not the letter (A..Z)
162 char path [MAX_PATH+1];
163 int drivenum = toupper(m_drive[0]) - 'A' + 1;
164 if (_getdcwd(drivenum, path, MAX_PATH+1))
165 {
166 // the path includes the drive so we have the drive info twice
167 // need to prepend this absolute path to the spec such that any remaining relative path is still retained
168 if (!is_separator(path[strlen(path)-1])) spec.insert(2, 1, preferred_separator);
169 spec.insert(2, path+2);
170 }
171 else
172 {
173 // non-existent drive - fill in just the root directory
174 spec.insert(2, 1, preferred_separator);
175 }
176 }
177 }
178 else if (spec.size() >= 2 && is_separator(spec[0]) && is_separator(spec[1]))
179 {
180 // found an UNC prefix
181 i = 2;
182 // find the end of the prefix by scanning for the next seperator or the end of the spec
183 while (i < spec.size() && !is_separator(spec[i])) i++;
184 m_drive = spec.substr(0, i);
185 m_relative = false;
186 }
187 #endif
188 #ifdef CYGWIN
189 // first split off the drive letter or UNC prefix on Windows - the Cygwin environment supports these too
190 if (spec.size() >= 2 && isalpha(spec[0]) && spec[1] == ':')
191 {
192 // found a drive letter
193 i = 2;
194 m_drive = spec.substr(0, 2);
195 m_relative = false;
196 // if there is a drive but no path or a relative path, get the current
197 // path for this drive and prepend it to the path
198 if (i == spec.size() || !is_separator(spec[i]))
199 {
200 // non-existent drive - fill in just the root directory
201 spec.insert(2, 1, preferred_separator);
202 }
203 }
204 else if (spec.size() >= 2 && is_separator(spec[0]) && is_separator(spec[1]))
205 {
206 // found an UNC prefix
207 i = 2;
208 // find the end of the prefix by scanning for the next seperator or the end of the spec
209 while (i < spec.size() && !is_separator(spec[i])) i++;
210 m_drive = spec.substr(0, i);
211 m_relative = false;
212 }
213 #endif
214 // check whether the path is absolute or relative and discard the leading / if absolute
215 if (i < spec.size() && is_separator(spec[i]))
216 {
217 m_relative = false;
218 i++;
219 #ifdef MSWINDOWS
220 // if there's no drive, fill it in on Windows since absolute paths must have a drive
221 if (m_drive.empty())
222 {
223 m_drive += (char)(_getdrive() - 1 + 'A');
224 m_drive += ':';
225 }
226 #endif
227 }
228 // now extract the path elements - note that a trailing / is not significant since /a/b/c/ === /a/b/c
229 // also note that the leading / has been discarded - all paths are relative
230 // if absolute() is set, then paths are relative to the drive, else they are relative to the current path
231 unsigned start = i;
232 while(i <= spec.size())
233 {
234 if (i == spec.size())
235 {
236 // path element terminated by the end of the string
237 // discard this element if it is zero length because that represents the trailing /
238 if (i != start)
239 m_path.push_back(spec.substr(start, i-start));
240 }
241 else if (is_separator(spec[i]))
242 {
243 // path element terminated by a separator
244 m_path.push_back(spec.substr(start, i-start));
245 start = i+1;
246 }
247 i++;
248 }
249 // TODO - some error handling?
250 return true;
251 }
252
253 bool file_specification::initialise_file(const std::string& spec)
254 {
255 m_filename.erase();
256 // remove last element as the file and then treat the rest as a folder
257 unsigned i = spec.size();
258 while (--i)
259 {
260 if (is_separator(spec[i]))
261 break;
262 #ifdef MSWINDOWS
263 // on windoze you can say a:fred.txt so the colon separates the path from the filename
264 else if (i == 1 && spec[i] == ':')
265 break;
266 #endif
267 }
268 bool result = initialise_folder(spec.substr(0,i+1));
269 m_filename = spec.substr(i+1,spec.size()-i-1);
270 // TODO - some error handling?
271 return result;
272 }
273
274 bool file_specification::simplify(void)
275 {
276 // simplify the path by removing unnecessary . and .. entries - Note that zero-length entries are treated like .
277 for (unsigned i = 0; i < m_path.size(); )
278 {
279 if (m_path[i].empty() || m_path[i].compare(".") == 0)
280 {
281 // found . or null
282 // these both mean do nothing - so simply delete this element
283 m_path.erase(m_path.begin()+i);
284 }
285 else if (m_path[i].compare("..") == 0)
286 {
287 // found ..
288 if (i == 0 && !m_relative)
289 {
290 // up from the root does nothing so can be deleted
291 m_path.erase(m_path.begin()+i);
292 i++;
293 }
294 else if (i == 0 || m_path[i-1].compare("..") == 0)
295 {
296 // the first element of a relative path or the previous element is .. then keep it
297 i++;
298 }
299 else
300 {
301 // otherwise delete this element and the previous one
302 m_path.erase(m_path.begin()+i);
303 m_path.erase(m_path.begin()+i-1);
304 i--;
305 }
306 }
307 // keep all other elements
308 else
309 i++;
310 }
311 // TODO - error checking?
312 return true;
313 }
314
315 bool file_specification::make_absolute(const std::string& root)
316 {
317 // test whether already an absolute path in which case there's nothing to do
318 if (absolute()) return true;
319 // now simply call the other version of make_absolute
320 file_specification rootspec;
321 rootspec.initialise_folder(root);
322 return make_absolute(rootspec);
323 }
324
325 bool file_specification::make_absolute(const file_specification& rootspec)
326 {
327 // test whether already an absolute path in which case there's nothing to do
328 if (absolute()) return true;
329 // initialise the result with the root and make the root absolute
330 file_specification result = rootspec;
331 result.make_absolute();
332 // now append this's relative path and filename to the root's absolute path
333 for (unsigned i = 0; i < subpath_size(); i++)
334 result.add_subpath(subpath_element(i));
335 result.set_file(file());
336 // now the result is the absolute path, so transfer it to this
337 *this = result;
338 // and simplify to get rid of any unwanted .. or . elements
339 simplify();
340 return true;
341 }
342
343 bool file_specification::make_relative(const std::string& root)
344 {
345 // test whether already an relative path in which case there's nothing to do
346 if (relative()) return true;
347 // now simply call the other version of make_relative
348 file_specification rootspec;
349 rootspec.initialise_folder(root);
350 return make_relative(rootspec);
351 }
352
353 bool file_specification::make_relative(const file_specification& rootspec)
354 {
355 // test whether already an relative path in which case there's nothing to do
356 if (relative()) return true;
357 // initialise the result with the root and make the root absolute
358 file_specification absolute_root = rootspec;
359 absolute_root.make_absolute();
360 // now compare elements of the absolute root with elements of this to find the common path
361 // if the drives are different, no conversion can take place and the result must be absolute, else clear the drive
362 if (!path_compare(drive(), absolute_root.drive())) return true;
363 set_drive("");
364 // first remove leading elements that are identical to the corresponding element in root
365 unsigned i = 0;
366 while(subpath_size() > 0 &&
367 i < absolute_root.subpath_size() &&
368 path_compare(subpath_element(0), absolute_root.subpath_element(i)))
369 {
370 subpath_erase(0);
371 i++;
372 }
373 // now add a .. prefix for every element in root that is different from this
374 while (i < absolute_root.subpath_size())
375 {
376 m_path.insert(m_path.begin(), "..");
377 i++;
378 }
379 set_relative();
380 return true;
381 }
382
383 std::string file_specification::image(void) const
384 {
385 std::string result = m_drive;
386 if (absolute())
387 result += preferred_separator;
388 if (!m_path.empty())
389 {
390 for (unsigned i = 0; i < m_path.size(); i++)
391 {
392 if (i != 0) result += std::string(1,preferred_separator);
393 result += m_path[i];
394 }
395 }
396 else if (relative())
397 result += '.';
398 // add a trailing / to the last directory element
399 if (result.empty() || !is_separator(result[result.size()-1]))
400 result += preferred_separator;
401 if (!m_filename.empty())
402 result += m_filename;
403 return result;
404 }
405
406 ////////////////////////////////////////////////////////////////////////////////
407 // classifying functions
408
409 // Under both Windows and Unix, the stat function is used for classification
410
411 // Under Linux, the following classifications are defined
412 // source: Linux man page for stat(2) http://linux.die.net/man/2/stat
413 // S_IFMT 0170000 bitmask for the file type bitfields
414 // S_IFSOCK 0140000 socket (Note this overlaps with S_IFDIR)
415 // S_IFLNK 0120000 symbolic link
416 // S_IFREG 0100000 regular file
417 // S_IFBLK 0060000 block device
418 // S_IFDIR 0040000 directory
419 // S_IFCHR 0020000 character device
420 // S_IFIFO 0010000 FIFO
421 // There are also some Posix-standard macros:
422 // S_ISREG(m) is it a regular file?
423 // S_ISDIR(m) directory?
424 // S_ISCHR(m) character device?
425 // S_ISBLK(m) block device?
426 // S_ISFIFO(m) FIFO (named pipe)?
427 // S_ISLNK(m) symbolic link? (Not in POSIX.1-1996.)
428 // S_ISSOCK(m) socket? (Not in POSIX.1-1996.)
429 // Under Windows, the following are defined:
430 // source: Header file sys/stat.h distributed with Visual Studio 10
431 // _S_IFMT (S_IFMT) 0xF000 file type mask
432 // _S_IFREG (S_IFREG) 0x8000 regular
433 // _S_IFDIR (S_IFDIR) 0x4000 directory
434 // _S_IFCHR (S_IFCHR) 0x2000 character special
435 // _S_IFIFO 0x1000 pipe
436
437 #ifdef MSWINDOWS
438 // file type tests are not defined for some reason on Windows despite them providing the stat() function!
439 #define R_OK 4
440 #define W_OK 2
441 // Posix-style macros for Windows
442 #ifndef S_ISREG
443 #define S_ISREG(mode) ((mode & _S_IFMT) == _S_IFREG)
444 #endif
445 #ifndef S_ISDIR
446 #define S_ISDIR(mode) ((mode & _S_IFMT) == _S_IFDIR)
447 #endif
448 #ifndef S_ISCHR
449 #define S_ISCHR(mode) ((mode & _S_IFMT) == _S_IFCHR)
450 #endif
451 #ifndef S_ISBLK
452 #define S_ISBLK(mode) (false)
453 #endif
454 #ifndef S_ISFIFO
455 #define S_ISFIFO(mode) ((mode & _S_IFMT) == _S_IFIFO)
456 #endif
457 #ifndef S_ISLNK
458 #define S_ISLNK(mode) (false)
459 #endif
460 #ifndef S_ISSOCK
461 #define S_ISSOCK(mode) (false)
462 #endif
463 #endif
464
465 bool is_present (const std::string& thing)
466 {
467 // strip off any trailing separator because that will cause the stat function to fail
468 std::string path = thing;
469 if (!path.empty() && is_separator(path[path.size()-1]))
470 path.erase(path.size()-1,1);
471 // now test if this thing exists using the built-in stat function
472 struct stat buf;
473 return stat(path.c_str(), &buf) == 0;
474 }
475
476 bool is_folder (const std::string& thing)
477 {
478 // strip off any trailing separator because that will cause the stat function to fail
479 std::string path = thing;
480 if (!path.empty() && is_separator(path[path.size()-1]))
481 path.erase(path.size()-1,1);
482 // now test if this thing exists using the built-in stat function and if so, is it a folder
483 struct stat buf;
484 if (!(stat(path.c_str(), &buf) == 0))
485 return false;
486 // If the object is present, see if it is a directory
487 // this is the Posix-approved way of testing
488 return S_ISDIR(buf.st_mode);
489 }
490
491 bool is_file (const std::string& thing)
492 {
493 // strip off any trailing separator because that will cause the stat function to fail
494 std::string path = thing;
495 if (!path.empty() && is_separator(path[path.size()-1]))
496 path.erase(path.size()-1,1);
497 // now test if this thing exists using the built-in stat function and if so, is it a file
498 struct stat buf;
499 if (!(stat(path.c_str(), &buf) == 0))
500 return false;
501 // If the object is present, see if it is a file or file-like object
502 // Note that devices are neither folders nor files
503 // this is the Posix-approved way of testing
504 return S_ISREG(buf.st_mode) || S_ISLNK(buf.st_mode) || S_ISSOCK(buf.st_mode) || S_ISFIFO(buf.st_mode);
505 }
506
507 ////////////////////////////////////////////////////////////////////////////////
508 // file functions
509
510 bool file_exists (const std::string& filespec)
511 {
512 return is_file(filespec);
513 }
514
515 bool file_readable (const std::string& filespec)
516 {
517 // a file is readable if it exists and can be read
518 if (!file_exists(filespec)) return false;
519 return access(filespec.c_str(),R_OK)==0;
520 }
521
522 bool file_writable (const std::string& filespec)
523 {
524 // a file is writable if it exists as a file and is writable or if
525 // it doesn't exist but could be created and would be writable
526 if (is_present(filespec))
527 {
528 if (!is_file(filespec)) return false;
529 return access(filespec.c_str(),W_OK)==0;
530 }
531 std::string dir = folder_part(filespec);
532 if (dir.empty()) dir = ".";
533 return folder_writable(dir);
534 }
535
536 size_t file_size (const std::string& filespec)
537 {
538 struct stat buf;
539 if (!(stat(filespec.c_str(), &buf) == 0)) return 0;
540 return buf.st_size;
541 }
542
543 bool file_delete (const std::string& filespec)
544 {
545 if (!is_file(filespec)) return false;
546 return remove(filespec.c_str())==0;
547 }
548
549 bool file_rename (const std::string& old_filespec, const std::string& new_filespec)
550 {
551 if (!is_file(old_filespec)) return false;
552 return rename(old_filespec.c_str(), new_filespec.c_str())==0;
553 }
554
555 bool file_copy (const std::string& old_filespec, const std::string& new_filespec)
556 {
557 if (!is_file(old_filespec)) return false;
558 // do an exact copy - to do this, use binary mode
559 bool result = true;
560 FILE* old_file = fopen(old_filespec.c_str(),"rb");
561 FILE* new_file = fopen(new_filespec.c_str(),"wb");
562 if (!old_file)
563 result = false;
564 else if (!new_file)
565 result = false;
566 else
567 {
568 for (int byte = getc(old_file); byte != EOF; byte = getc(old_file))
569 putc(byte,new_file);
570 }
571 if (old_file) fclose(old_file);
572 if (new_file) fclose(new_file);
573 return result;
574 }
575
576 bool file_move (const std::string& old_filespec, const std::string& new_filespec)
577 {
578 // try to move the file by renaming - if that fails then do a copy and delete the original
579 if (file_rename(old_filespec, new_filespec))
580 return true;
581 if (!file_copy(old_filespec, new_filespec))
582 return false;
583 // I'm not sure what to do if the delete fails - is that an error?
584 // I've made it an error and then delete the copy so that the original state is recovered
585 if (file_delete(old_filespec))
586 return true;
587 file_delete(new_filespec);
588 return false;
589 }
590
591 time_t file_created (const std::string& filespec)
592 {
593 struct stat buf;
594 if (!(stat(filespec.c_str(), &buf) == 0)) return 0;
595 return buf.st_ctime;
596 }
597
598 time_t file_modified (const std::string& filespec)
599 {
600 struct stat buf;
601 if (!(stat(filespec.c_str(), &buf) == 0)) return 0;
602 return buf.st_mtime;
603 }
604
605 time_t file_accessed (const std::string& filespec)
606 {
607 struct stat buf;
608 if (!(stat(filespec.c_str(), &buf) == 0)) return 0;
609 return buf.st_atime;
610 }
611
612 std::string create_filespec (const std::string& directory, const std::string& filename)
613 {
614 std::string result = directory;
615 // if directory is empty then no directory part will be added
616 // add trailing slash if the directory was specified and does not have a trailing slash
617 if (!result.empty() && !is_separator(result[result.size()-1]))
618 result += preferred_separator;
619 // if filename is null or empty, nothing will be added so the path is then a directory path
620 result += filename;
621 return result;
622 }
623
624 std::string create_filespec (const std::string& directory, const std::string& basename, const std::string& extension)
625 {
626 return create_filespec(directory, create_filename(basename, extension));
627 }
628
629 std::string create_filename(const std::string& basename, const std::string& extension)
630 {
631 std::string name = basename;
632 // extension is optional - so the dot is also optional
633 if (!extension.empty())
634 {
635 if (extension[0] != '.') name += '.';
636 name += extension;
637 }
638 return name;
639 }
640
641 ////////////////////////////////////////////////////////////////////////////////
642 // folder functions
643
644 bool folder_create (const std::string& directory)
645 {
646 #ifdef MSWINDOWS
647 return mkdir(directory.c_str()) == 0;
648 #else
649 return mkdir(directory.c_str(), 0777) == 0;
650 #endif
651 }
652
653 bool folder_exists (const std::string& directory)
654 {
655 return is_folder(directory);
656 }
657
658 bool folder_readable (const std::string& directory)
659 {
660 // a folder is readable if it exists and has read access
661 std::string dir = directory;
662 if (dir.empty()) dir = ".";
663 if (!folder_exists(dir)) return false;
664 return access(dir.c_str(),R_OK)==0;
665 }
666
667 bool folder_writable (const std::string& directory)
668 {
669 // a folder is writable if it exists and has write access
670 std::string dir = directory;
671 if (dir.empty()) dir = ".";
672 if (!folder_exists(dir)) return false;
673 return access(dir.c_str(),W_OK)==0;
674 }
675
676 bool folder_delete (const std::string& directory, bool recurse)
677 {
678 std::string dir = directory;
679 if (dir.empty()) dir = ".";
680 if (!folder_exists(dir)) return false;
681 bool result = true;
682 // depth-first traversal ensures that directory contents are deleted before trying to delete the directory itself
683 if (recurse)
684 {
685 std::vector<std::string> subdirectories = folder_subdirectories(dir);
686 for (std::vector<std::string>::size_type d = 0; d < subdirectories.size(); ++d)
687 if (!folder_delete(folder_down(dir,subdirectories[d]),true))
688 result = false;
689 std::vector<std::string> files = folder_files(dir);
690 for (std::vector<std::string>::size_type f = 0; f < files.size(); ++f)
691 if (!file_delete(create_filespec(dir, files[f])))
692 result = false;
693 }
694 if (rmdir(dir.c_str())!=0) result = false;
695 return result;
696 }
697
698 bool folder_rename (const std::string& old_directory, const std::string& new_directory)
699 {
700 if (!folder_exists(old_directory)) return false;
701 return rename(old_directory.c_str(), new_directory.c_str())==0;
702 }
703
704 bool folder_empty(const std::string& directory)
705 {
706 std::string dir = directory.empty() ? std::string(".") : directory;
707 bool result = true;
708 #ifdef MSWINDOWS
709 std::string wildcard = create_filespec(dir, "*.*");
710 long handle = -1;
711 _finddata_t fileinfo;
712 for (bool OK = (handle = _findfirst((char*)wildcard.c_str(), &fileinfo)) != -1; OK; OK = (_findnext(handle, &fileinfo)==0))
713 {
714 std::string strentry = fileinfo.name;
715 if (strentry.compare(".")!=0 && strentry.compare("..")!=0)
716 {
717 result = false;
718 break;
719 }
720 }
721 _findclose(handle);
722 #else
723 DIR* d = opendir(dir.c_str());
724 if (d)
725 {
726 for (dirent* entry = readdir(d); entry; entry = readdir(d))
727 {
728 std::string strentry = entry->d_name;
729 if (strentry.compare(".")!=0 && strentry.compare("..")!=0)
730 {
731 result = false;
732 break;
733 }
734 }
735 closedir(d);
736 }
737 #endif
738 return result;
739 }
740
741 bool folder_set_current(const std::string& folder)
742 {
743 if (!folder_exists(folder))
744 return false;
745 #ifdef MSWINDOWS
746 // Windose implementation - this returns non-zero for success
747 return (SetCurrentDirectoryA(folder.c_str()) != 0);
748 #else
749 // Unix implementation - this returns zero for success
750 return (chdir(folder.c_str()) == 0);
751 #endif
752 }
753
754 std::string folder_current (void)
755 {
756 return ".";
757 }
758
759 std::string folder_current_full(void)
760 {
761 // It's not clear from the documentation whether the buffer for a path should be one byte longer
762 // than the maximum path length to allow for the null termination, so I have made it so anyway
763 #ifdef MSWINDOWS
764 char abspath [MAX_PATH+1];
765 return std::string(_fullpath(abspath, ".", MAX_PATH+1));
766 #else
767 char pathname [MAXPATHLEN+1];
768 char* result = getcwd(pathname,MAXPATHLEN+1);
769 if (!result)
770 {
771 // should really report the error from errno
772 return std::string();
773 }
774 return std::string(result);
775 #endif
776 }
777
778 std::string folder_down (const std::string& directory, const std::string& subdirectory)
779 {
780 file_specification spec;
781 spec.initialise_folder(directory);
782 spec.add_subpath(subdirectory);
783 return spec.image();
784 }
785
786 std::string folder_up (const std::string& directory, unsigned levels)
787 {
788 file_specification spec;
789 spec.initialise_folder(directory);
790 for (unsigned i = 0; i < levels; i++)
791 spec.add_subpath("..");
792 spec.simplify();
793 return spec.image();
794 }
795
796 std::vector<std::string> folder_subdirectories (const std::string& directory)
797 {
798 return folder_wildcard(directory, "*", true, false);
799 }
800
801 std::vector<std::string> folder_files (const std::string& directory)
802 {
803 return folder_wildcard(directory, "*", false, true);
804 }
805
806 std::vector<std::string> folder_all(const std::string& directory)
807 {
808 return folder_wildcard(directory, "*", true, true);
809 }
810
811 std::vector<std::string> folder_wildcard (const std::string& directory, const std::string& wild, bool subdirs, bool files)
812 {
813 std::string dir = directory.empty() ? std::string(".") : directory;
814 std::vector<std::string> results;
815 #ifdef MSWINDOWS
816 std::string wildcard = create_filespec(dir, wild);
817 long handle = -1;
818 _finddata_t fileinfo;
819 for (bool OK = (handle = _findfirst((char*)wildcard.c_str(), &fileinfo)) != -1; OK; OK = (_findnext(handle, &fileinfo)==0))
820 {
821 std::string strentry = fileinfo.name;
822 if (strentry.compare(".")!=0 && strentry.compare("..")!=0)
823 if ((subdirs && (fileinfo.attrib & _A_SUBDIR)) || (files && !(fileinfo.attrib & _A_SUBDIR)))
824 results.push_back(strentry);
825 }
826 _findclose(handle);
827 #else
828 DIR* d = opendir(dir.c_str());
829 if (d)
830 {
831 for (dirent* entry = readdir(d); entry; entry = readdir(d))
832 {
833 std::string strentry = entry->d_name;
834 if (strentry.compare(".")!=0 && strentry.compare("..")!=0)
835 {
836 std::string subpath = create_filespec(dir, strentry);
837 if (((subdirs && is_folder(subpath)) || (files && is_file(subpath))) && (wildcard(wild, strentry)))
838 results.push_back(strentry);
839 }
840 }
841 closedir(d);
842 }
843 #endif
844 return results;
845 }
846
847 std::string folder_home (void)
848 {
849 if (getenv("HOME"))
850 return std::string(getenv("HOME"));
851 #ifdef MSWINDOWS
852 if (getenv("HOMEDRIVE") || getenv("HOMEPATH"))
853 return std::string(getenv("HOMEDRIVE")) + std::string(getenv("HOMEPATH"));
854 return "C:\\";
855 #else
856 if (getenv("USER"))
857 return folder_down("/home", std::string(getenv("USER")));
858 if (getenv("USERNAME"))
859 return folder_down("/home", std::string(getenv("USERNAME")));
860 return "";
861 #endif
862 }
863
864 ////////////////////////////////////////////////////////////////////////////////
865 // path functions convert between full and relative paths
866
867 bool is_full_path(const std::string& path)
868 {
869 file_specification spec;
870 spec.initialise_folder(path.empty() ? std::string(".") : path);
871 return spec.absolute();
872 }
873
874 bool is_relative_path(const std::string& path)
875 {
876 file_specification spec;
877 spec.initialise_folder(path.empty() ? std::string(".") : path);
878 return spec.relative();
879 }
880
881 static std::string full_path(const std::string& root, const std::string& path)
882 {
883 // convert path to a full path using root as the start point for relative paths
884 // decompose the path and test whether it is already an absolute path, in which case just return it
885 file_specification spec;
886 spec.initialise_folder(path.empty() ? std::string(".") : path);
887 if (spec.absolute()) return spec.image();
888 // okay, so the path is relative after all, so we need to combine it with the root path
889 // decompose the root path and check whether it is relative
890 file_specification rootspec;
891 rootspec.initialise_folder(root.empty() ? std::string(".") : root);
892 if (rootspec.relative())
893 rootspec.make_absolute();
894 // Now do the conversion of the path relative to the root
895 spec.make_absolute(rootspec);
896 return spec.image();
897 }
898
899 static std::string relative_path(const std::string& root, const std::string& path)
900 {
901 // convert path to a relative path, using the root path as its starting point
902 // first convert both paths to full paths relative to CWD
903 file_specification rootspec;
904 rootspec.initialise_folder(root.empty() ? std::string(".") : root);
905 if (rootspec.relative())
906 rootspec.make_absolute();
907 file_specification spec;
908 spec.initialise_folder(path.empty() ? std::string(".") : path);
909 if (spec.relative())
910 spec.make_absolute();
911 // now make path spec relative to the root spec
912 spec.make_relative(rootspec);
913 return spec.image();
914 }
915
916 std::string folder_to_path (const std::string& path, const std::string& directory)
917 {
918 return full_path(path, directory);
919 }
920
921 std::string filespec_to_path (const std::string& path, const std::string& spec)
922 {
923 return create_filespec(folder_to_path(path, folder_part(spec)),filename_part(spec));
924 }
925
926 std::string folder_to_path(const std::string& folder)
927 {
928 return folder_to_path(folder_current(), folder);
929 }
930
931 std::string filespec_to_path(const std::string& filespec)
932 {
933 return filespec_to_path(folder_current(), filespec);
934 }
935
936 std::string folder_to_relative_path(const std::string& root, const std::string& folder)
937 {
938 return relative_path(root, folder);
939 }
940
941 std::string filespec_to_relative_path(const std::string& root, const std::string& spec)
942 {
943 return create_filespec(folder_to_relative_path(root, folder_part(spec)),filename_part(spec));
944 }
945
946 std::string folder_to_relative_path(const std::string& folder)
947 {
948 return folder_to_relative_path(folder_current(), folder);
949 }
950
951 std::string filespec_to_relative_path(const std::string& filespec)
952 {
953 return filespec_to_relative_path(folder_current(), filespec);
954 }
955
956 std::string folder_append_separator(const std::string& folder)
957 {
958 std::string result = folder;
959 if (result.empty() || !is_separator(result[result.size()-1]))
960 result += preferred_separator;
961 return result;
962 }
963
964 ////////////////////////////////////////////////////////////////////////////////
965
966 std::string basename_part (const std::string& spec)
967 {
968 std::string fname = filename_part(spec);
969 // scan back through filename until a '.' is found and remove suffix
970 // the whole filename is the basename if there is no '.'
971 std::string::size_type i = fname.find_last_of('.');
972 // observe Unix convention that a dot at the start of a filename is part of the basename, not the extension
973 if (i != 0 && i != std::string::npos)
974 fname.erase(i, fname.size()-i);
975 return fname;
976 }
977
978 std::string filename_part (const std::string& spec)
979 {
980 // scan back through filename until a preferred_separator is found and remove prefix;
981 // if there is no preferred_separator then remove nothing, i.e. the whole filespec is filename
982 unsigned i = spec.size();
983 while (i--)
984 {
985 if (is_separator(spec[i]))
986 return spec.substr(i+1,spec.size()-i-1);
987 }
988 return spec;
989 }
990
991 std::string extension_part (const std::string& spec)
992 {
993 std::string fname = filename_part(spec);
994 // scan back through filename until a '.' is found and remove prefix;
995 std::string::size_type i = fname.find_last_of('.');
996 // observe Unix convention that a dot at the start of a filename is part of the name, not the extension;
997 if (i != 0 && i != std::string::npos)
998 fname.erase(0, i+1);
999 else
1000 fname.erase();
1001 return fname;
1002 }
1003
1004 std::string folder_part (const std::string& spec)
1005 {
1006 // scan back through filename until a separator is found and remove prefix
1007 // if there is no separator, remove the whole
1008 unsigned i = spec.size();
1009 while (i--)
1010 {
1011 if (is_separator(spec[i]))
1012 return spec.substr(0,i);
1013 }
1014 return std::string();
1015 }
1016
1017 std::vector<std::string> filespec_elements(const std::string& filespec)
1018 {
1019 file_specification spec;
1020 spec.initialise_file(filespec);
1021 std::vector<std::string> result = spec.path();
1022 if (!spec.drive().empty()) result.insert(result.begin(),spec.drive());
1023 if (!spec.file().empty()) result.push_back(spec.file());
1024 return result;
1025 }
1026
1027 std::vector<std::string> folder_elements(const std::string& folder)
1028 {
1029 file_specification spec;
1030 spec.initialise_folder(folder);
1031 std::vector<std::string> result = spec.path();
1032 if (!spec.drive().empty()) result.insert(result.begin(),spec.drive());
1033 return result;
1034 }
1035
1036 ////////////////////////////////////////////////////////////////////////////////
1037 // mimic the command lookup used by the shell
1038
1039 // Windows looks at the following locations:
1040 // 1) application root
1041 // 2) current directory
1042 // 3) 32-bit system directory
1043 // 4) 16-bit system directory
1044 // 5) windows system directory
1045 // 6) %path%
1046 // currently only (2) and (6) has been implemented although many system folders are on the path anyway
1047 // also implement the implied .exe extension on commands with no path (see CreateProcess documentation)
1048 // TODO - PATHEXT handling to find non-exe executables
1049
1050 std::string path_lookup (const std::string& command)
1051 {
1052 std::string path = std::string(".") + PATH_SPLITTER + getenv("PATH");
1053 return lookup(command, path);
1054 }
1055
1056 std::string lookup (const std::string& command, const std::string& path, const std::string& splitter)
1057 {
1058 // first check whether the command is already a path and check whether it exists
1059 if (!folder_part(command).empty())
1060 {
1061 if (file_exists(command))
1062 return command;
1063 }
1064 else
1065 {
1066 // command is just a name - so do path lookup
1067 // split the path into its elements
1068 std::vector<std::string> paths;
1069 if (!path.empty())
1070 {
1071 for(std::string::size_type offset = 0;;)
1072 {
1073 std::string::size_type found = path.find(splitter, offset);
1074 if (found != std::string::npos)
1075 {
1076 paths.push_back(path.substr(offset, found-offset));
1077 offset = found + splitter.size();
1078 }
1079 else
1080 {
1081 paths.push_back(path.substr(offset, path.size()-offset));
1082 break;
1083 }
1084 }
1085 }
1086 // now lookup each path to see if it its the matching one
1087 for (unsigned i = 0; i < paths.size(); i++)
1088 {
1089 std::string spec = create_filespec(paths[i], command);
1090 if (file_exists(spec))
1091 {
1092 return spec;
1093 }
1094 }
1095 }
1096 #ifdef MSWINDOWS
1097 // if there is no extension, try recursing on each possible extension
1098 // TODO iterate through PATHEXT
1099 if (extension_part(command).empty())
1100 return lookup(create_filespec(folder_part(command), basename_part(command), "exe"), path, splitter);
1101 #endif
1102 // if path lookup failed, return empty string to indicate error
1103 return std::string();
1104 }
1105
1106 ////////////////////////////////////////////////////////////////////////////////
1107
1108 std::string install_path(const std::string& argv0)
1109 {
1110 std::string bin_directory = folder_part(argv0);
1111 if (bin_directory.empty())
1112 {
1113 // do path lookup to find the executable path
1114 bin_directory = folder_part(path_lookup(argv0));
1115 }
1116 return bin_directory;
1117 }
1118
1119 ////////////////////////////////////////////////////////////////////////////////
1120
1121 } // end namespace stlplus
This page took 0.085196 seconds and 4 git commands to generate.