1 ////////////////////////////////////////////////////////////////////////////////
3 // Author: Andy Rushton
4 // Copyright: (c) Southampton University 1999-2004
5 // (c) Andy Rushton 2004-2009
6 // License: BSD License, see ../docs/license.html
8 // This is a portable interface to the file system.
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.
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
18 ////////////////////////////////////////////////////////////////////////////////
19 #include "file_system.hpp"
20 #include "wildcard.hpp"
34 #include <sys/types.h>
38 #include <sys/param.h>
41 #include <sys/types.h>
44 ////////////////////////////////////////////////////////////////////////////////
49 ////////////////////////////////////////////////////////////////////////////////
50 // definitions of separators
53 static const char* separator_set
= "\\/";
54 static const char preferred_separator
= '\\';
56 static const char* separator_set
= "/";
57 static const char preferred_separator
= '/';
60 static bool is_separator (char ch
)
62 for (int i
= 0; separator_set
[i
]; i
++)
64 if (separator_set
[i
] == ch
)
70 ////////////////////////////////////////////////////////////////////////////////
71 // implement string comparison of paths - Unix is case-sensitive, Windoze is case-insensitive
75 static std::string
lowercase(const std::string
& val
)
77 std::string text
= val
;
78 for (unsigned i
= 0; i
< text
.size(); i
++)
79 text
[i
] = tolower(text
[i
]);
85 bool path_compare(const std::string
& l
, const std::string
& r
)
88 return lowercase(l
) == lowercase(r
);
94 ////////////////////////////////////////////////////////////////////////////////
95 // Internal data structure used to hold the different parts of a filespec
97 class file_specification
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
106 file_specification(void) : m_relative(false) {}
107 ~file_specification(void) {}
109 bool initialise_folder(const std::string
& spec
);
110 bool initialise_file(const std::string
& spec
);
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;}
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
;}
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
;}
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
);}
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
;}
138 std::string
image(void) const;
141 bool file_specification::initialise_folder(const std::string
& folder_spec
)
143 std::string spec
= folder_spec
;
150 // first split off the drive letter or UNC prefix on Windows
151 if (spec
.size() >= 2 && isalpha(spec
[0]) && spec
[1] == ':')
153 // found a drive letter
155 m_drive
= spec
.substr(0, 2);
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
]))
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))
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);
173 // non-existent drive - fill in just the root directory
174 spec
.insert(2, 1, preferred_separator
);
178 else if (spec
.size() >= 2 && is_separator(spec
[0]) && is_separator(spec
[1]))
180 // found an UNC prefix
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
);
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] == ':')
192 // found a drive letter
194 m_drive
= spec
.substr(0, 2);
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
]))
200 // non-existent drive - fill in just the root directory
201 spec
.insert(2, 1, preferred_separator
);
204 else if (spec
.size() >= 2 && is_separator(spec
[0]) && is_separator(spec
[1]))
206 // found an UNC prefix
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
);
214 // check whether the path is absolute or relative and discard the leading / if absolute
215 if (i
< spec
.size() && is_separator(spec
[i
]))
220 // if there's no drive, fill it in on Windows since absolute paths must have a drive
223 m_drive
+= (char)(_getdrive() - 1 + 'A');
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
232 while(i
<= spec
.size())
234 if (i
== spec
.size())
236 // path element terminated by the end of the string
237 // discard this element if it is zero length because that represents the trailing /
239 m_path
.push_back(spec
.substr(start
, i
-start
));
241 else if (is_separator(spec
[i
]))
243 // path element terminated by a separator
244 m_path
.push_back(spec
.substr(start
, i
-start
));
249 // TODO - some error handling?
253 bool file_specification::initialise_file(const std::string
& spec
)
256 // remove last element as the file and then treat the rest as a folder
257 unsigned i
= spec
.size();
260 if (is_separator(spec
[i
]))
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
] == ':')
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?
274 bool file_specification::simplify(void)
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(); )
279 if (m_path
[i
].empty() || m_path
[i
].compare(".") == 0)
282 // these both mean do nothing - so simply delete this element
283 m_path
.erase(m_path
.begin()+i
);
285 else if (m_path
[i
].compare("..") == 0)
288 if (i
== 0 && !m_relative
)
290 // up from the root does nothing so can be deleted
291 m_path
.erase(m_path
.begin()+i
);
294 else if (i
== 0 || m_path
[i
-1].compare("..") == 0)
296 // the first element of a relative path or the previous element is .. then keep it
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);
307 // keep all other elements
311 // TODO - error checking?
315 bool file_specification::make_absolute(const std::string
& root
)
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
);
325 bool file_specification::make_absolute(const file_specification
& rootspec
)
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
338 // and simplify to get rid of any unwanted .. or . elements
343 bool file_specification::make_relative(const std::string
& root
)
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
);
353 bool file_specification::make_relative(const file_specification
& rootspec
)
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;
364 // first remove leading elements that are identical to the corresponding element in root
366 while(subpath_size() > 0 &&
367 i
< absolute_root
.subpath_size() &&
368 path_compare(subpath_element(0), absolute_root
.subpath_element(i
)))
373 // now add a .. prefix for every element in root that is different from this
374 while (i
< absolute_root
.subpath_size())
376 m_path
.insert(m_path
.begin(), "..");
383 std::string
file_specification::image(void) const
385 std::string result
= m_drive
;
387 result
+= preferred_separator
;
390 for (unsigned i
= 0; i
< m_path
.size(); i
++)
392 if (i
!= 0) result
+= std::string(1,preferred_separator
);
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
;
406 ////////////////////////////////////////////////////////////////////////////////
407 // classifying functions
410 // file type tests are not defined for some reason on Windows despite them providing the stat() function!
415 bool is_present (const std::string
& thing
)
417 // strip off any trailing separator because that will cause the stat function to fail
418 std::string path
= thing
;
419 if (!path
.empty() && is_separator(path
[path
.size()-1]))
420 path
.erase(path
.size()-1,1);
421 // now test if this thing exists using the built-in stat function
423 return stat(path
.c_str(), &buf
) == 0;
426 bool is_folder (const std::string
& thing
)
428 // strip off any trailing separator because that will cause the stat function to fail
429 std::string path
= thing
;
430 if (!path
.empty() && is_separator(path
[path
.size()-1]))
431 path
.erase(path
.size()-1,1);
432 // now test if this thing exists using the built-in stat function and if so, is it a folder
434 if (!(stat(path
.c_str(), &buf
) == 0)) {return false;}
435 return (buf
.st_mode
& S_IFDIR
) != 0;
438 bool is_file (const std::string
& thing
)
440 // strip off any trailing separator because that will cause the stat function to fail
441 std::string path
= thing
;
442 if (!path
.empty() && is_separator(path
[path
.size()-1]))
443 path
.erase(path
.size()-1,1);
444 // now test if this thing exists using the built-in stat function and if so, is it a file
446 if (!(stat(path
.c_str(), &buf
) == 0)) {return false;}
447 return (buf
.st_mode
& S_IFREG
) != 0;
450 ////////////////////////////////////////////////////////////////////////////////
453 bool file_exists (const std::string
& filespec
)
455 return is_file(filespec
);
458 bool file_readable (const std::string
& filespec
)
460 // a file is readable if it exists and can be read
461 if (!file_exists(filespec
)) return false;
462 return access(filespec
.c_str(),R_OK
)==0;
465 bool file_writable (const std::string
& filespec
)
467 // a file is writable if it exists as a file and is writable or if it doesn't exist but could be created and would be writable
468 if (is_present(filespec
))
470 if (!is_file(filespec
)) return false;
471 return access(filespec
.c_str(),W_OK
)==0;
473 std::string dir
= folder_part(filespec
);
474 if (dir
.empty()) dir
= ".";
475 return folder_writable(dir
);
478 size_t file_size (const std::string
& filespec
)
481 if (!(stat(filespec
.c_str(), &buf
) == 0)) return 0;
485 bool file_delete (const std::string
& filespec
)
487 if (!is_file(filespec
)) return false;
488 return remove(filespec
.c_str())==0;
491 bool file_rename (const std::string
& old_filespec
, const std::string
& new_filespec
)
493 if (!is_file(old_filespec
)) return false;
494 return rename(old_filespec
.c_str(), new_filespec
.c_str())==0;
497 bool file_copy (const std::string
& old_filespec
, const std::string
& new_filespec
)
499 if (!is_file(old_filespec
)) return false;
500 // do an exact copy - to do this, use binary mode
502 FILE* old_file
= fopen(old_filespec
.c_str(),"rb");
503 FILE* new_file
= fopen(new_filespec
.c_str(),"wb");
510 for (int byte
= getc(old_file
); byte
!= EOF
; byte
= getc(old_file
))
513 if (old_file
) fclose(old_file
);
514 if (new_file
) fclose(new_file
);
518 bool file_move (const std::string
& old_filespec
, const std::string
& new_filespec
)
520 // try to move the file by renaming - if that fails then do a copy and delete the original
521 if (file_rename(old_filespec
, new_filespec
))
523 if (!file_copy(old_filespec
, new_filespec
))
525 // I'm not sure what to do if the delete fails - is that an error?
526 // I've made it an error and then delete the copy so that the original state is recovered
527 if (file_delete(old_filespec
))
529 file_delete(new_filespec
);
533 time_t file_created (const std::string
& filespec
)
536 if (!(stat(filespec
.c_str(), &buf
) == 0)) return 0;
540 time_t file_modified (const std::string
& filespec
)
543 if (!(stat(filespec
.c_str(), &buf
) == 0)) return 0;
547 time_t file_accessed (const std::string
& filespec
)
550 if (!(stat(filespec
.c_str(), &buf
) == 0)) return 0;
554 std::string
create_filespec (const std::string
& directory
, const std::string
& filename
)
556 std::string result
= directory
;
557 // if directory is empty then no directory part will be added
558 // add trailing slash if the directory was specified and does not have a trailing slash
559 if (!result
.empty() && !is_separator(result
[result
.size()-1]))
560 result
+= preferred_separator
;
561 // if filename is null or empty, nothing will be added so the path is then a directory path
566 std::string
create_filespec (const std::string
& directory
, const std::string
& basename
, const std::string
& extension
)
568 return create_filespec(directory
, create_filename(basename
, extension
));
571 std::string
create_filename(const std::string
& basename
, const std::string
& extension
)
573 std::string name
= basename
;
574 // extension is optional - so the dot is also optional
575 if (!extension
.empty())
577 if (extension
[0] != '.') name
+= '.';
583 ////////////////////////////////////////////////////////////////////////////////
586 bool folder_create (const std::string
& directory
)
589 return mkdir(directory
.c_str()) == 0;
591 return mkdir(directory
.c_str(), 0777) == 0;
595 bool folder_exists (const std::string
& directory
)
597 return is_folder(directory
);
600 bool folder_readable (const std::string
& directory
)
602 // a folder is readable if it exists and has read access
603 std::string dir
= directory
;
604 if (dir
.empty()) dir
= ".";
605 if (!folder_exists(dir
)) return false;
606 return access(dir
.c_str(),R_OK
)==0;
609 bool folder_writable (const std::string
& directory
)
611 // a folder is writable if it exists and has write access
612 std::string dir
= directory
;
613 if (dir
.empty()) dir
= ".";
614 if (!folder_exists(dir
)) return false;
615 return access(dir
.c_str(),W_OK
)==0;
618 bool folder_delete (const std::string
& directory
, bool recurse
)
620 std::string dir
= directory
;
621 if (dir
.empty()) dir
= ".";
622 if (!folder_exists(dir
)) return false;
624 // depth-first traversal ensures that directory contents are deleted before trying to delete the directory itself
627 std::vector
<std::string
> subdirectories
= folder_subdirectories(dir
);
628 for (std::vector
<std::string
>::size_type d
= 0; d
< subdirectories
.size(); ++d
)
629 if (!folder_delete(folder_down(dir
,subdirectories
[d
]),true))
631 std::vector
<std::string
> files
= folder_files(dir
);
632 for (std::vector
<std::string
>::size_type f
= 0; f
< files
.size(); ++f
)
633 if (!file_delete(create_filespec(dir
, files
[f
])))
636 if (rmdir(dir
.c_str())!=0) result
= false;
640 bool folder_rename (const std::string
& old_directory
, const std::string
& new_directory
)
642 if (!folder_exists(old_directory
)) return false;
643 return rename(old_directory
.c_str(), new_directory
.c_str())==0;
646 bool folder_empty(const std::string
& directory
)
648 std::string dir
= directory
.empty() ? std::string(".") : directory
;
651 std::string wildcard
= create_filespec(dir
, "*.*");
653 _finddata_t fileinfo
;
654 for (bool OK
= (handle
= _findfirst((char*)wildcard
.c_str(), &fileinfo
)) != -1; OK
; OK
= (_findnext(handle
, &fileinfo
)==0))
656 std::string strentry
= fileinfo
.name
;
657 if (strentry
.compare(".")!=0 && strentry
.compare("..")!=0)
665 DIR* d
= opendir(dir
.c_str());
668 for (dirent
* entry
= readdir(d
); entry
; entry
= readdir(d
))
670 std::string strentry
= entry
->d_name
;
671 if (strentry
.compare(".")!=0 && strentry
.compare("..")!=0)
683 bool folder_set_current(const std::string
& folder
)
685 if (!folder_exists(folder
))
688 // Windose implementation - this returns non-zero for success
689 return (SetCurrentDirectoryA(folder
.c_str()) != 0);
691 // Unix implementation - this returns zero for success
692 return (chdir(folder
.c_str()) == 0);
696 std::string
folder_current (void)
701 std::string
folder_current_full(void)
703 // It's not clear from the documentation whether the buffer for a path should be one byte longer
704 // than the maximum path length to allow for the null termination, so I have made it so anyway
706 char abspath
[MAX_PATH
+1];
707 return std::string(_fullpath(abspath
, ".", MAX_PATH
+1));
709 char pathname
[MAXPATHLEN
+1];
710 getcwd(pathname
,MAXPATHLEN
+1);
711 return std::string(pathname
);
715 std::string
folder_down (const std::string
& directory
, const std::string
& subdirectory
)
717 file_specification spec
;
718 spec
.initialise_folder(directory
);
719 spec
.add_subpath(subdirectory
);
723 std::string
folder_up (const std::string
& directory
, unsigned levels
)
725 file_specification spec
;
726 spec
.initialise_folder(directory
);
727 for (unsigned i
= 0; i
< levels
; i
++)
728 spec
.add_subpath("..");
733 std::vector
<std::string
> folder_subdirectories (const std::string
& directory
)
735 return folder_wildcard(directory
, "*", true, false);
738 std::vector
<std::string
> folder_files (const std::string
& directory
)
740 return folder_wildcard(directory
, "*", false, true);
743 std::vector
<std::string
> folder_all(const std::string
& directory
)
745 return folder_wildcard(directory
, "*", true, true);
748 std::vector
<std::string
> folder_wildcard (const std::string
& directory
, const std::string
& wild
, bool subdirs
, bool files
)
750 std::string dir
= directory
.empty() ? std::string(".") : directory
;
751 std::vector
<std::string
> results
;
753 std::string wildcard
= create_filespec(dir
, wild
);
755 _finddata_t fileinfo
;
756 for (bool OK
= (handle
= _findfirst((char*)wildcard
.c_str(), &fileinfo
)) != -1; OK
; OK
= (_findnext(handle
, &fileinfo
)==0))
758 std::string strentry
= fileinfo
.name
;
759 if (strentry
.compare(".")!=0 && strentry
.compare("..")!=0)
760 if ((subdirs
&& (fileinfo
.attrib
& _A_SUBDIR
)) || (files
&& !(fileinfo
.attrib
& _A_SUBDIR
)))
761 results
.push_back(strentry
);
765 DIR* d
= opendir(dir
.c_str());
768 for (dirent
* entry
= readdir(d
); entry
; entry
= readdir(d
))
770 std::string strentry
= entry
->d_name
;
771 if (strentry
.compare(".")!=0 && strentry
.compare("..")!=0)
773 std::string subpath
= create_filespec(dir
, strentry
);
774 if (((subdirs
&& is_folder(subpath
)) || (files
&& is_file(subpath
))) && (wildcard(wild
, strentry
)))
775 results
.push_back(strentry
);
784 std::string
folder_home (void)
787 return std::string(getenv("HOME"));
789 if (getenv("HOMEDRIVE") || getenv("HOMEPATH"))
790 return std::string(getenv("HOMEDRIVE")) + std::string(getenv("HOMEPATH"));
794 return folder_down("/home", std::string(getenv("USER")));
795 if (getenv("USERNAME"))
796 return folder_down("/home", std::string(getenv("USERNAME")));
801 ////////////////////////////////////////////////////////////////////////////////
802 // path functions convert between full and relative paths
804 bool is_full_path(const std::string
& path
)
806 file_specification spec
;
807 spec
.initialise_folder(path
.empty() ? std::string(".") : path
);
808 return spec
.absolute();
811 bool is_relative_path(const std::string
& path
)
813 file_specification spec
;
814 spec
.initialise_folder(path
.empty() ? std::string(".") : path
);
815 return spec
.relative();
818 static std::string
full_path(const std::string
& root
, const std::string
& path
)
820 // convert path to a full path using root as the start point for relative paths
821 // decompose the path and test whether it is already an absolute path, in which case just return it
822 file_specification spec
;
823 spec
.initialise_folder(path
.empty() ? std::string(".") : path
);
824 if (spec
.absolute()) return spec
.image();
825 // okay, so the path is relative after all, so we need to combine it with the root path
826 // decompose the root path and check whether it is relative
827 file_specification rootspec
;
828 rootspec
.initialise_folder(root
.empty() ? std::string(".") : root
);
829 if (rootspec
.relative())
830 rootspec
.make_absolute();
831 // Now do the conversion of the path relative to the root
832 spec
.make_absolute(rootspec
);
836 static std::string
relative_path(const std::string
& root
, const std::string
& path
)
838 // convert path to a relative path, using the root path as its starting point
839 // first convert both paths to full paths relative to CWD
840 file_specification rootspec
;
841 rootspec
.initialise_folder(root
.empty() ? std::string(".") : root
);
842 if (rootspec
.relative())
843 rootspec
.make_absolute();
844 file_specification spec
;
845 spec
.initialise_folder(path
.empty() ? std::string(".") : path
);
847 spec
.make_absolute();
848 // now make path spec relative to the root spec
849 spec
.make_relative(rootspec
);
853 std::string
folder_to_path (const std::string
& path
, const std::string
& directory
)
855 return full_path(path
, directory
);
858 std::string
filespec_to_path (const std::string
& path
, const std::string
& spec
)
860 return create_filespec(folder_to_path(path
, folder_part(spec
)),filename_part(spec
));
863 std::string
folder_to_path(const std::string
& folder
)
865 return folder_to_path(folder_current(), folder
);
868 std::string
filespec_to_path(const std::string
& filespec
)
870 return filespec_to_path(folder_current(), filespec
);
873 std::string
folder_to_relative_path(const std::string
& root
, const std::string
& folder
)
875 return relative_path(root
, folder
);
878 std::string
filespec_to_relative_path(const std::string
& root
, const std::string
& spec
)
880 return create_filespec(folder_to_relative_path(root
, folder_part(spec
)),filename_part(spec
));
883 std::string
folder_to_relative_path(const std::string
& folder
)
885 return folder_to_relative_path(folder_current(), folder
);
888 std::string
filespec_to_relative_path(const std::string
& filespec
)
890 return filespec_to_relative_path(folder_current(), filespec
);
893 std::string
folder_append_separator(const std::string
& folder
)
895 std::string result
= folder
;
896 if (result
.empty() || !is_separator(result
[result
.size()-1]))
897 result
+= preferred_separator
;
901 ////////////////////////////////////////////////////////////////////////////////
903 std::string
basename_part (const std::string
& spec
)
905 std::string fname
= filename_part(spec
);
906 // scan back through filename until a '.' is found and remove suffix
907 // the whole filename is the basename if there is no '.'
908 std::string::size_type i
= fname
.find_last_of('.');
909 // observe Unix convention that a dot at the start of a filename is part of the basename, not the extension
910 if (i
!= 0 && i
!= std::string::npos
)
911 fname
.erase(i
, fname
.size()-i
);
915 std::string
filename_part (const std::string
& spec
)
917 // scan back through filename until a preferred_separator is found and remove prefix;
918 // if there is no preferred_separator then remove nothing, i.e. the whole filespec is filename
919 unsigned i
= spec
.size();
922 if (is_separator(spec
[i
]))
923 return spec
.substr(i
+1,spec
.size()-i
-1);
928 std::string
extension_part (const std::string
& spec
)
930 std::string fname
= filename_part(spec
);
931 // scan back through filename until a '.' is found and remove prefix;
932 std::string::size_type i
= fname
.find_last_of('.');
933 // observe Unix convention that a dot at the start of a filename is part of the name, not the extension;
934 if (i
!= 0 && i
!= std::string::npos
)
941 std::string
folder_part (const std::string
& spec
)
943 // scan back through filename until a separator is found and remove prefix
944 // if there is no separator, remove the whole
945 unsigned i
= spec
.size();
948 if (is_separator(spec
[i
]))
949 return spec
.substr(0,i
);
951 return std::string();
954 std::vector
<std::string
> filespec_elements(const std::string
& filespec
)
956 file_specification spec
;
957 spec
.initialise_file(filespec
);
958 std::vector
<std::string
> result
= spec
.path();
959 if (!spec
.drive().empty()) result
.insert(result
.begin(),spec
.drive());
960 if (!spec
.file().empty()) result
.push_back(spec
.file());
964 std::vector
<std::string
> folder_elements(const std::string
& folder
)
966 file_specification spec
;
967 spec
.initialise_folder(folder
);
968 std::vector
<std::string
> result
= spec
.path();
969 if (!spec
.drive().empty()) result
.insert(result
.begin(),spec
.drive());
973 ////////////////////////////////////////////////////////////////////////////////
974 // mimic the command lookup used by the shell
976 // Windows looks at the following locations:
977 // 1) application root
978 // 2) current directory
979 // 3) 32-bit system directory
980 // 4) 16-bit system directory
981 // 5) windows system directory
983 // currently only (2) and (6) has been implemented although many system folders are on the path anyway
984 // also implement the implied .exe extension on commands with no path (see CreateProcess documentation)
985 // TODO - PATHEXT handling to find non-exe executables
987 std::string
path_lookup (const std::string
& command
)
989 std::string path
= std::string(".") + PATH_SPLITTER
+ getenv("PATH");
990 return lookup(command
, path
);
993 std::string
lookup (const std::string
& command
, const std::string
& path
, const std::string
& splitter
)
995 // first check whether the command is already a path and check whether it exists
996 if (!folder_part(command
).empty())
998 if (file_exists(command
))
1003 // command is just a name - so do path lookup
1004 // split the path into its elements
1005 std::vector
<std::string
> paths
;
1008 for(std::string::size_type offset
= 0;;)
1010 std::string::size_type found
= path
.find(splitter
, offset
);
1011 if (found
!= std::string::npos
)
1013 paths
.push_back(path
.substr(offset
, found
-offset
));
1014 offset
= found
+ splitter
.size();
1018 paths
.push_back(path
.substr(offset
, path
.size()-offset
));
1023 // now lookup each path to see if it its the matching one
1024 for (unsigned i
= 0; i
< paths
.size(); i
++)
1026 std::string spec
= create_filespec(paths
[i
], command
);
1027 if (file_exists(spec
))
1034 // if there is no extension, try recursing on each possible extension
1035 // TODO iterate through PATHEXT
1036 if (extension_part(command
).empty())
1037 return lookup(create_filespec(folder_part(command
), basename_part(command
), "exe"), path
, splitter
);
1039 // if path lookup failed, return empty string to indicate error
1040 return std::string();
1043 ////////////////////////////////////////////////////////////////////////////////
1045 std::string
install_path(const std::string
& argv0
)
1047 std::string bin_directory
= folder_part(argv0
);
1048 if (bin_directory
.empty())
1050 // do path lookup to find the executable path
1051 bin_directory
= folder_part(path_lookup(argv0
));
1053 return bin_directory
;
1056 ////////////////////////////////////////////////////////////////////////////////
1058 } // end namespace stlplus