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 
21 int 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 
30 int 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 
55 void 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 
70 bool 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 
105 bool 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 
112 bool 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.
137 WvString 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 
162 bool 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 
177 bool 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.
187 bool 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
222 int 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 
276 FILE *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 
291 WvString 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 
312 mode_t get_umask()
313 {
314  mode_t rv = umask(0);
315  umask(rv);
316 
317  return rv;
318 }
319 
WvString::edit
char * edit()
make the string editable, and return a non-const (char*)
Definition: wvstring.h:397
WvFile
WvFile implements a stream connected to a file or Unix device.
Definition: wvfile.h:28
WvString
WvString is an implementation of a simple and efficient printable-string class.
Definition: wvstring.h:329
WvDirIter
Definition: wvdiriter.h:31
WvStringList
This is a WvList of WvStrings, and is a really handy way to parse strings.
Definition: wvstringlist.h:27