globcasematch takes a std::string, not Path
[libs/core.git] / source / utils.cpp
1 /*
2 This file is part of libmsppath
3 Copyright © 2006  Mikko Rasa, Mikkosoft Productions
4 Distributed under the LGPL
5 */
6 #include <dirent.h>
7 #include <sys/stat.h>
8 #include <errno.h>
9 #ifndef WIN32
10 #include <fnmatch.h>
11 #else
12 #include <msp/strings/glob.h>
13 #endif
14 #include <msp/strings/utils.h>
15 #include "path.h"
16 #include "utils.h"
17
18 using namespace std;
19
20 namespace Msp {
21 namespace Path {
22
23 /**
24 Fixes the case of the given path to match files / directories on the
25 filesystem.  Intended to be used in programs that need to interact with
26 emulated Windows programs.
27 */
28 Path fix_case(const Path &path)
29 {
30         bool found=true;
31         Path result;
32         for(Path::iterator i=path.begin(); i!=path.end(); ++i)
33         {
34                 if(!found || *i=="/")
35                         result/=*i;
36                 else
37                 {
38                         list<string> files;
39                         if(result.size())
40                                 files=list_files(result);
41                         else
42                                 files=list_files(".");
43
44                         found=false;
45                         for(list<string>::iterator j=files.begin(); (j!=files.end() && !found); ++j)
46                                 if(!strcasecmp(*j,*i))
47                                 {
48                                         result/=*j;
49                                         found=true;
50                                 }
51
52                         if(!found)
53                                 result/=*i;
54                 }
55         }
56
57         return result;
58 }
59
60 /**
61 Creates the given directory and any parent directories if needed.
62
63 @param   path  The path to create
64 @param   mode  Access mode for new directories
65
66 @return  0 on success, -1 on error
67 */
68 int mkpath(const Path &path, int mode)
69 {
70         Path p;
71         for(Path::iterator i=path.begin(); i!=path.end(); ++i)
72         {
73                 p/=*i;
74 #ifdef WIN32
75                 if(p.size()==1 && is_windows_drive(*i)) continue;
76 #endif
77                 struct stat st;
78                 int err=stat(p.str().c_str(),&st);
79                 if(err==0)
80                 {
81                         if(!S_ISDIR(st.st_mode))
82                         {
83                                 errno=EEXIST;
84                                 return -1;
85                         }
86                         continue;
87                 }
88                 else if(errno!=ENOENT)
89                         return -1;
90                 else
91                 {
92 #ifdef WIN32
93                         // The win32 version of this function doesn't take the mode argument.  Go figure.
94                         (void)mode;
95                         err=mkdir(p.str().c_str());
96 #else
97                         err=mkdir(p.str().c_str(),mode);
98 #endif
99                         if(err==-1) return -1;
100                 }
101         }
102
103         return 0;
104 }
105
106 int rmdir(const Path &path, bool recursive)
107 {
108         if(recursive)
109         {
110                 list<string> files=list_files(path);
111                 for(list<string>::iterator i=files.begin(); i!=files.end(); ++i)
112                 {
113                         Path p=path/ *i;
114                         struct stat st;
115                         stat(p.str().c_str(),&st);
116                         int err=0;
117                         if(S_ISDIR(st.st_mode))
118                                 err=rmdir(p,true);
119                         else
120                                 err=unlink(p.str().c_str());
121                         if(err) return err;
122                 }
123         }
124         return rmdir(path.str().c_str());
125 }
126
127 /**
128 Lists all files in a directory except the implied . and .. entries.
129 */
130 list<string> list_files(const Path &path)
131 {
132         list<string> result;
133         DIR *dir=opendir(path.str().c_str());
134         if(dir)
135         {
136                 while(dirent *de=readdir(dir))
137                 {
138                         const char *fn=de->d_name;
139                         if(fn[0]=='.' && (fn[1]==0 || (fn[1]=='.' && fn[2]==0))) 
140                                 continue;
141                         result.push_back(fn);
142                 }
143                 closedir(dir);
144         }
145         return result;
146 }
147
148 bool exists(const Path &path)
149 {
150         struct stat st;
151         return !stat(path.str().c_str(), &st);
152 }
153
154 Filename splitext(const string &fn)
155 {
156         Filename result;
157         unsigned dot=fn.rfind('.');
158         result.base=fn.substr(0, dot);
159         if(dot!=string::npos)
160                 result.ext=fn.substr(dot);
161         return result;
162 }
163
164 int fnmatch(const string &pat, const Path &fn)
165 {
166 #ifdef WIN32
167         return globcasematch(pat, fn.str());
168 #else
169         return ::fnmatch(pat.c_str(), fn.str().c_str(), FNM_PATHNAME);
170 #endif
171 }
172
173 Path relative(const Path &path, const Path &base)
174 {
175         Path::iterator i=path.begin();
176         Path::iterator j=base.begin();
177         for(; (i!=path.end() && j!=base.end() && *i==*j); ++i,++j);
178
179         Path result;
180         for(; j!=base.end(); ++j)
181                 result/="..";
182         for(; i!=path.end(); ++i)
183                 result/=*i;
184
185         return result;
186 }
187
188 /**
189 Extracts the basename from the given path.  Same thing as Path::Path(p)[-1],
190 but faster.
191 */
192 string basename(const std::string &p)
193 {
194         unsigned slash=p.rfind(DIRCHAR);
195         if(slash==string::npos)
196                 return p;
197         else
198                 return p.substr(slash+1);
199 }
200
201 } // namespace Path
202 } // namespace Msp