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