WvStreams
fileutils.cc
1/*
2 * Worldvisions Weaver Software:
3 * Copyright (C) 1997-2002 Net Integration Technologies, Inc.
4 *
5 * Various useful file utilities.
6 *
7 */
8#include "fileutils.h"
9#include "wvfile.h"
10#include "wvdiriter.h"
11#include <string.h>
12#include <sys/stat.h>
13#ifndef _WIN32
14#include <fnmatch.h>
15#endif
16#ifndef _MSC_VER
17#include <unistd.h>
18#include <utime.h>
19#endif
20
21int wvmkdir(WvStringParm _dir, int create_mode)
22{
23#ifdef _WIN32
24 return mkdir(_dir);
25#else
26 return mkdir(_dir, create_mode);
27#endif
28}
29
30int mkdirp(WvStringParm _dir, int create_mode)
31{
32 if (!access(_dir, X_OK))
33 return 0;
34
35 // You're trying to make a nothing directory eh?
36 assert(!!_dir);
37
38 WvString dir(_dir);
39 char *p = dir.edit();
40
41 while ((p = strchr(++p, '/')))
42 {
43 *p = '\0';
44 if (access(dir, X_OK) && wvmkdir(dir, create_mode))
45 return -1;
46 *p = '/';
47 }
48
49 // You're probably creating the directory to write to it? Maybe this should
50 // look for R_OK&X_OK instead of X_OK&W_OK...
51 return (access(dir, X_OK&W_OK) && wvmkdir(dir, create_mode)) ? -1 : 0;
52}
53
54
55void rm_rf(WvStringParm dir)
56{
57 WvDirIter i(dir, false, false); // non-recursive, don't skip_mounts
58 for (i.rewind(); i.next(); )
59 {
60 if (i.isdir())
61 rm_rf(i->fullname);
62 else
63 ::unlink(i->fullname);
64 }
65 ::rmdir(dir);
66 ::unlink(dir);
67}
68
69
70bool fcopy(WvStringParm src, WvStringParm dst)
71{
72 struct stat buf;
73 if (stat(src, &buf))
74 return false;
75
76 WvFile in(src, O_RDONLY);
77 unlink(dst);
78
79 int oldmode = umask(0);
80 WvFile out(dst, O_CREAT|O_WRONLY, buf.st_mode & 007777);
81 umask(oldmode);
82
83 in.autoforward(out);
84 while (in.isok() && out.isok())
85 {
86 /* This used to be a select(0), but really, if select() returns
87 * false, it'll keep doing it until the end of time. If you're
88 * going into an infinite loop, better save the CPU a bit, since
89 * you can still find out about it with strace... */
90 if (in.select(-1, true, false))
91 in.callback();
92 }
93 if (!out.isok())
94 return false;
95
96 struct utimbuf utim;
97 utim.actime = utim.modtime = buf.st_mtime;
98 if (utime(dst, &utim))
99 return false;
100
101 return true;
102}
103
104
105bool fcopy(WvStringParm srcdir, WvStringParm dstdir, WvStringParm relname)
106{
107 return fcopy(WvString("%s/%s", srcdir, relname),
108 WvString("%s/%s", dstdir, relname));
109}
110
111
112bool ftouch(WvStringParm file, time_t mtime)
113{
114 if (!WvFile(file, O_WRONLY|O_CREAT).isok())
115 return false;
116
117 struct utimbuf *buf = NULL;
118 if (mtime != 0)
119 {
120 buf = (struct utimbuf *)malloc(sizeof(struct utimbuf));
121 buf->actime = time(NULL);
122 buf->modtime = mtime;
123 }
124
125 if (utime(file, buf) == 0)
126 {
127 free(buf);
128 return true;
129 }
130
131 free(buf);
132 return false;
133}
134
135
136// Reads the contents of a symlink. Returns WvString::null on error.
137WvString wvreadlink(WvStringParm path)
138{
139#ifdef _WIN32
140 return WvString::null; // no such thing as a symlink on Windows
141#else
142 WvString result;
143 int size = 64;
144 for (;;)
145 {
146 result.setsize(size);
147 int readlink_result = readlink(path, result.edit(), size);
148 if (readlink_result == -1)
149 return WvString::null;
150 if (readlink_result < size)
151 {
152 result.edit()[readlink_result] = '\0';
153 break;
154 }
155 size = 2*size; // increase buffer size
156 }
157 return result;
158#endif
159}
160
161
162bool samedate(WvStringParm file1, WvStringParm file2)
163{
164 struct stat buf;
165 struct stat buf2;
166
167 if (stat(file1, &buf) || stat(file2, &buf2))
168 return false;
169
170 if (buf.st_mtime == buf2.st_mtime || buf.st_ctime == buf2.st_ctime)
171 return true;
172
173 return false;
174}
175
176
177bool samedate(WvStringParm dir1, WvStringParm dir2, WvStringParm relname)
178{
179 return samedate(WvString("%s/%s", dir1, relname),
180 WvString("%s/%s", dir2, relname));
181}
182
183
184#ifndef _WIN32
185// runs fnmatch against everything in patterns. We also interpret
186// CVS-style '!' patterns, which makes us very fancy.
187bool wvfnmatch(WvStringList& patterns, WvStringParm name, int flags)
188{
189 WvStringList::Iter i(patterns);
190 bool match = false;
191
192 for (i.rewind(); i.next(); )
193 {
194 // if we hit JUST a '!', reset any matches found so far.
195 if (*i == "!") {
196 match = false;
197 continue;
198 }
199
200 // if we hit something that starts with '!', we unmatch anything
201 // found so far.
202 if (i->cstr()[0] == '!')
203 {
204 if (!match)
205 continue; // nothing to unmatch, so why try?
206 if (fnmatch(*i+1, name, flags) == 0) // matches
207 match = false; // unmatch it.
208 }
209 else
210 {
211 // just a straightforward matching case.
212 if (fnmatch(*i, name, flags) == 0) // matches
213 match = true;
214 }
215 }
216
217 return match;
218}
219#endif
220
221#ifndef _WIN32 // file permissions are too screwy in win32
222int wvchmod(const char *path, mode_t mode)
223{
224 struct stat st;
225 if (lstat(path, &st) == -1) {
226 return -1;
227 }
228
229 int filedes = open(path, O_RDONLY);
230 if (filedes == -1) {
231 // if we're not running as root, this file/dir may have 0
232 // perms and open() fails, so let's try again
233 //
234 // NOTE: This is not as secure as the proper way, since
235 // it's conceivable that someone swaps out the dir/file
236 // for a symlink between our check and the chmod() call
237 //
238 struct stat sst;
239 if (getuid() != 0)
240 if (stat(path, &sst) != -1)
241 if (st.st_ino == sst.st_ino)
242 return chmod(path, mode);
243
244 return -1;
245 }
246
247 struct stat fst;
248 if (fstat(filedes, &fst) == -1) {
249 close(filedes);
250 return -1;
251 }
252
253 if (st.st_ino != fst.st_ino) {
254 close(filedes);
255 return -1;
256 }
257
258#ifndef _WIN32
259 // we're definitely chmod'ing the open file here, which is good,
260 // because the filename itself might have been moved around between
261 // our stat'ing and chmod'ing it.
262 int retval = fchmod(filedes, mode);
263#else
264 // this is guaranteed to be the same file as filedes, because in
265 // Windows, open files can't be changed on the filesystem (unlike in
266 // Unix).
267 int retval = chmod(path, mode);
268#endif
269 close(filedes);
270
271 return retval;
272}
273#endif // !_WIN32
274
275
276FILE *wvtmpfile()
277{
278#ifndef _WIN32 // tmpfile() is really the best choice, when it works
279 return tmpfile();
280#else
281 // in win32, tmpfile() creates files in c:\...
282 // and that directory isn't always writable! Idiots.
283 char *name = _tempnam("c:\\temp", "wvtmp");
284 FILE *f = fopen(name, "wb+");
285 free(name);
286 return f;
287#endif
288}
289
290
291WvString wvtmpfilename(WvStringParm prefix)
292{
293#ifndef _WIN32 // tmpfile() is really the best choice, when it works
294 WvString tmpname("/tmp/%sXXXXXX", prefix);
295 int fd;
296 if ((fd = mkstemp(tmpname.edit())) == (-1))
297 return WvString();
298 close(fd);
299#else
300 WvString tmpname(_tempnam("c:\\temp", prefix.cstr()));
301 int fd;
302 fd = open(tmpname, O_WRONLY|O_CREAT|O_EXCL, 0777);
303 if (fd < 0)
304 return WvString::null; // weird
305 _close(fd);
306#endif
307
308 return tmpname;
309}
310
311
312mode_t get_umask()
313{
314 mode_t rv = umask(0);
315 umask(rv);
316
317 return rv;
318}
319
A WvFastString acts exactly like a WvString, but can take (const char *) strings without needing to a...
const char * cstr() const
return a (const char *) for this string.
WvFile implements a stream connected to a file or Unix device.
This is a WvList of WvStrings, and is a really handy way to parse strings.
WvString is an implementation of a simple and efficient printable-string class.
char * edit()
make the string editable, and return a non-const (char*)