1 /*
2  * IceWM
3  *
4  * Copyright (C) 2004,2005 Marko Macek
5  */
6 #include "config.h"
7 
8 #include "upath.h"
9 #include <unistd.h>
10 #include <sys/types.h>
11 #include <sys/stat.h>
12 #include <stdio.h>
13 #include <fcntl.h>
14 #include <fnmatch.h>
15 #include "base.h"
16 #include "yapp.h"
17 
18 const mstring upath::slash("/");
19 const upath upath::rootPath(slash);
20 
isSeparator(int ch) const21 bool upath::isSeparator(int ch) const {
22     return ch == '/';
23 }
24 
parent() const25 upath upath::parent() const {
26     int len = length();
27     int sep = fPath.lastIndexOf('/');
28     while (0 < sep && 1 + sep == len) {
29         len = sep;
30         sep = fPath.substring(0, size_t(len)).lastIndexOf('/');
31     }
32     len = max(0, sep);
33     while (len > 1 && isSeparator(fPath[len - 1])) {
34         --len;
35     }
36     return upath( fPath.substring(0, size_t(len)) );
37 }
38 
name() const39 mstring upath::name() const {
40     int start = 1 + fPath.lastIndexOf('/');
41     return fPath.substring(size_t(start), size_t(length() - start));
42 }
43 
relative(const upath & npath) const44 upath upath::relative(const upath &npath) const {
45     if (npath.isEmpty())
46         return *this;
47     else if (isEmpty()) {
48         return npath;
49     }
50     else if (isSeparator(path()[length() - 1])) {
51         if (isSeparator(npath.path()[0]))
52             return upath(path() + npath.path().substring(1));
53         else
54             return upath(path() + npath.path());
55     }
56     else if (isSeparator(npath.path()[0]))
57         return upath(path() + npath.path());
58     else
59         return upath(path() + slash + npath.path());
60 }
61 
child(const char * npath) const62 upath upath::child(const char *npath) const {
63     return relative(upath(npath));
64 }
65 
addExtension(const char * ext) const66 upath upath::addExtension(const char *ext) const {
67     return upath(path().append(ext));
68 }
69 
getExtension() const70 mstring upath::getExtension() const {
71     int dot = path().lastIndexOf('.');
72     int sep = path().lastIndexOf('/');
73     if (dot > sep + 1 && dot + 1 < length())
74         return path().substring(size_t(dot));
75     return null;
76 }
77 
removeExtension() const78 upath upath::removeExtension() const {
79     return fPath.substring(0, size_t(length()) - getExtension().length());
80 }
81 
replaceExtension(const char * ext) const82 upath upath::replaceExtension(const char* ext) const {
83     return removeExtension().addExtension(ext);
84 }
85 
expand() const86 mstring upath::expand() const {
87     int c = fPath[0];
88     if (c == '~') {
89         int k = fPath[1];
90         if (k == -1 || isSeparator(k))
91             return (upath(userhome(nullptr)) +
92                     fPath.substring(size_t(min(2, length())))).fPath;
93     }
94     else if (c == '$') {
95         mstring m(fPath.match("^\\$[_A-Za-z][_A-Za-z0-9]*"));
96         if (m.nonempty()) {
97             const char* e = getenv(m.substring(1));
98             if (e && *e && *e != '~' && *e != '$') {
99                 return e + fPath.substring(m.length());
100             }
101         }
102     }
103     return fPath;
104 }
105 
isAbsolute() const106 bool upath::isAbsolute() const {
107     int c = path()[0];
108     if (isSeparator(c))
109         return true;
110     if (c == '~' || c == '$') {
111         c = expand()[0];
112         if (isSeparator(c))
113             return true;
114     }
115     return false;
116 }
117 
isRelative() const118 bool upath::isRelative() const {
119     return false == isAbsolute() && false == hasProtocol();
120 }
121 
fileExists()122 bool upath::fileExists() {
123     struct stat sb;
124     return stat(&sb) == 0 && S_ISREG(sb.st_mode);
125 }
126 
fileSize()127 off_t upath::fileSize() {
128     struct stat sb;
129     return stat(&sb) == 0 ? sb.st_size : off_t(-1);
130 }
131 
dirExists()132 bool upath::dirExists() {
133     struct stat sb;
134     return stat(&sb) == 0 && S_ISDIR(sb.st_mode);
135 }
136 
isReadable()137 bool upath::isReadable() {
138     return access(R_OK) == 0;
139 }
140 
access(int mode)141 int upath::access(int mode) {
142     return ::access(string(), mode);
143 }
144 
isWritable()145 bool upath::isWritable() {
146     return access(W_OK) == 0;
147 }
148 
isExecutable()149 bool upath::isExecutable() {
150     return access(X_OK) == 0;
151 }
152 
mkdir(int mode)153 int upath::mkdir(int mode) {
154     return ::mkdir(string(), mode_t(mode));
155 }
156 
open(int flags,int mode)157 int upath::open(int flags, int mode) {
158     return ::open(string(), flags, mode);
159 }
160 
fopen(const char * mode)161 FILE* upath::fopen(const char *mode) {
162     return ::fopen(string(), mode);
163 }
164 
stat(struct stat * st)165 int upath::stat(struct stat *st) {
166     return ::stat(string(), st);
167 }
168 
remove()169 int upath::remove() {
170     return ::remove(string());
171 }
172 
renameAs(mstring dest)173 int upath::renameAs(mstring dest) {
174     return ::rename(string(), dest);
175 }
176 
fnMatch(const char * pattern,int flags)177 int upath::fnMatch(const char* pattern, int flags) {
178     return fnmatch(pattern, string(), flags);
179 }
180 
loadText()181 fcsmart upath::loadText() {
182     return filereader(expand()).read_all();
183 }
184 
copyFrom(upath from,int mode)185 bool upath::copyFrom(upath from, int mode) {
186     auto text = from.loadText();
187     if (text == nullptr)
188         return false;
189     int fd = open(O_WRONLY | O_CREAT | O_TRUNC, mode);
190     if (fd == -1)
191         return false;
192     size_t len = strlen(text);
193     ssize_t wr = write(fd, text, len);
194     close(fd);
195     return 0 <= wr && size_t(wr) == len;
196 }
197 
testWritable(int mode)198 bool upath::testWritable(int mode) {
199     int fd = open(O_WRONLY | O_CREAT | O_APPEND, mode);
200     if (0 <= fd) close(fd);
201     return 0 <= fd;
202 }
203 
hasProtocol() const204 bool upath::hasProtocol() const {
205     int k = path().indexOf('/');
206     return k > 0 && path()[k-1] == ':' && path()[k+1] == '/';
207 }
208 
isHttp() const209 bool upath::isHttp() const {
210     return path().startsWith("http") && hasProtocol();
211 }
212 
equals(const upath & s) const213 bool upath::equals(const upath &s) const {
214     if (path() != null && s.path() != null)
215         return path().equals(s.path());
216     else
217         return path() == null && s.path() == null;
218 }
219 
220 #include <glob.h>
221 #include "yarray.h"
222 
hasglob(mstring pattern)223 bool upath::hasglob(mstring pattern) {
224     const char* s = pattern;
225     while (*s && *s != '*' && *s != '?' && *s != '[')
226         s += 1 + (*s == '\\' && s[1]);
227     return *s != 0;
228 }
229 
glob(mstring pattern,YStringArray & list,const char * flags)230 bool upath::glob(mstring pattern, YStringArray& list, const char* flags) {
231     bool okay = false;
232     int flagbits = 0;
233     int (*const errfunc) (const char *epath, int eerrno) = nullptr;
234     glob_t gl = {};
235 
236     if (flags) {
237         for (int i = 0; flags[i]; ++i) {
238             switch (flags[i]) {
239                 case '/': flagbits |= GLOB_MARK; break;
240                 case 'C': flagbits |= GLOB_NOCHECK; break;
241                 case 'E': flagbits |= GLOB_NOESCAPE; break;
242                 case 'S': flagbits |= GLOB_NOSORT; break;
243                 default: break;
244             }
245         }
246     }
247 
248     if (0 == ::glob(pattern, flagbits, errfunc, &gl)) {
249         double limit = 1e6;
250         if (gl.gl_pathc < limit) {
251             int count = int(gl.gl_pathc);
252             list.clear();
253             list.setCapacity(count);
254             for (int i = 0; i < count; ++i) {
255                 list.append(gl.gl_pathv[i]);
256             }
257             okay = true;
258         }
259         globfree(&gl);
260     }
261     return okay;
262 }
263 
264 // vim: set sw=4 ts=4 et:
265