WvStreams
fileutils.cc
00001 /*
00002  * Worldvisions Weaver Software:
00003  *   Copyright (C) 1997-2002 Net Integration Technologies, Inc.
00004  * 
00005  * Various useful file utilities.
00006  *
00007  */
00008 #include "fileutils.h"
00009 #include "wvfile.h"
00010 #include "wvdiriter.h"
00011 #include <string.h>
00012 #include <sys/stat.h>
00013 #ifndef _WIN32
00014 #include <fnmatch.h>
00015 #endif
00016 #ifndef _MSC_VER
00017 #include <unistd.h>
00018 #include <utime.h>
00019 #endif
00020 
00021 int wvmkdir(WvStringParm _dir, int create_mode)
00022 {
00023 #ifdef _WIN32
00024     return mkdir(_dir);
00025 #else
00026     return mkdir(_dir, create_mode);
00027 #endif
00028 }
00029 
00030 int mkdirp(WvStringParm _dir, int create_mode)
00031 {
00032     if (!access(_dir, X_OK))
00033         return 0;
00034 
00035     // You're trying to make a nothing directory eh?
00036     assert(!!_dir);
00037 
00038     WvString dir(_dir);
00039     char *p = dir.edit();
00040 
00041     while ((p = strchr(++p, '/')))
00042     {
00043         *p = '\0';
00044         if (access(dir, X_OK) && wvmkdir(dir, create_mode))
00045             return -1;
00046         *p = '/';
00047     }
00048 
00049     // You're probably creating the directory to write to it? Maybe this should
00050     // look for R_OK&X_OK instead of X_OK&W_OK...
00051     return (access(dir, X_OK&W_OK) && wvmkdir(dir, create_mode)) ? -1 : 0;
00052 }
00053 
00054 
00055 void rm_rf(WvStringParm dir)
00056 {
00057     WvDirIter i(dir, false, false); // non-recursive, don't skip_mounts
00058     for (i.rewind(); i.next(); )
00059     {
00060         if (i.isdir())
00061             rm_rf(i->fullname);
00062         else
00063             ::unlink(i->fullname);
00064     }
00065     ::rmdir(dir);
00066     ::unlink(dir);
00067 }
00068 
00069 
00070 bool fcopy(WvStringParm src, WvStringParm dst)
00071 {
00072     struct stat buf;
00073     if (stat(src, &buf))
00074         return false;
00075 
00076     WvFile in(src, O_RDONLY);
00077     unlink(dst);
00078 
00079     int oldmode = umask(0);
00080     WvFile out(dst, O_CREAT|O_WRONLY, buf.st_mode & 007777);
00081     umask(oldmode);
00082 
00083     in.autoforward(out);
00084     while (in.isok() && out.isok())
00085     {
00086         /* This used to be a select(0), but really, if select() returns
00087          * false, it'll keep doing it until the end of time. If you're
00088          * going into an infinite loop, better save the CPU a bit, since
00089          * you can still find out about it with strace... */
00090         if (in.select(-1, true, false))
00091             in.callback();
00092     }
00093     if (!out.isok())
00094         return false;
00095 
00096     struct utimbuf utim;
00097     utim.actime = utim.modtime = buf.st_mtime;
00098     if (utime(dst, &utim))
00099         return false;
00100 
00101     return true;
00102 }
00103 
00104 
00105 bool fcopy(WvStringParm srcdir, WvStringParm dstdir, WvStringParm relname)
00106 {
00107     return fcopy(WvString("%s/%s", srcdir, relname),
00108         WvString("%s/%s", dstdir, relname));
00109 }
00110 
00111 
00112 bool ftouch(WvStringParm file, time_t mtime)
00113 {
00114     if (!WvFile(file, O_WRONLY|O_CREAT).isok())
00115         return false;
00116 
00117     struct utimbuf *buf = NULL;
00118     if (mtime != 0)
00119     {
00120         buf = (struct utimbuf *)malloc(sizeof(struct utimbuf));
00121         buf->actime = time(NULL);
00122         buf->modtime = mtime;
00123     }
00124 
00125     if (utime(file, buf) == 0)
00126     {
00127         free(buf);
00128         return true;
00129     }
00130 
00131     free(buf);
00132     return false;
00133 }
00134 
00135 
00136 // Reads the contents of a symlink.  Returns WvString::null on error.
00137 WvString wvreadlink(WvStringParm path)
00138 {
00139 #ifdef _WIN32
00140     return WvString::null; // no such thing as a symlink on Windows
00141 #else
00142     WvString result;
00143     int size = 64;
00144     for (;;)
00145     {
00146         result.setsize(size);
00147         int readlink_result = readlink(path, result.edit(), size);
00148         if (readlink_result == -1)
00149             return WvString::null;
00150         if (readlink_result < size)
00151         {
00152             result.edit()[readlink_result] = '\0';
00153             break;
00154         }
00155         size = 2*size; // increase buffer size
00156     }
00157     return result;
00158 #endif
00159 }
00160 
00161 
00162 bool samedate(WvStringParm file1, WvStringParm file2)
00163 {
00164     struct stat buf;
00165     struct stat buf2;
00166 
00167     if (stat(file1, &buf) || stat(file2, &buf2))
00168         return false;
00169 
00170     if (buf.st_mtime == buf2.st_mtime || buf.st_ctime == buf2.st_ctime)
00171         return true;
00172 
00173     return false;
00174 }
00175 
00176 
00177 bool samedate(WvStringParm dir1, WvStringParm dir2, WvStringParm relname)
00178 {
00179     return samedate(WvString("%s/%s", dir1, relname),
00180         WvString("%s/%s", dir2, relname));
00181 }
00182 
00183 
00184 #ifndef _WIN32
00185 // runs fnmatch against everything in patterns.  We also interpret 
00186 // CVS-style '!' patterns, which makes us very fancy.
00187 bool wvfnmatch(WvStringList& patterns, WvStringParm name, int flags)
00188 {
00189     WvStringList::Iter i(patterns);
00190     bool match = false;
00191 
00192     for (i.rewind(); i.next(); )
00193     {
00194         // if we hit JUST a '!', reset any matches found so far.
00195         if (*i == "!") {
00196             match = false;
00197             continue;
00198         }
00199 
00200         // if we hit something that starts with '!', we unmatch anything
00201         // found so far.
00202         if (i->cstr()[0] == '!')
00203         {
00204             if (!match)
00205                 continue;   // nothing to unmatch, so why try?
00206             if (fnmatch(*i+1, name, flags) == 0)    // matches
00207                 match = false;                      // unmatch it.
00208         }
00209         else
00210         {
00211             // just a straightforward matching case.
00212             if (fnmatch(*i, name, flags) == 0)  // matches
00213                 match = true;
00214         }
00215     }
00216 
00217     return match;
00218 }
00219 #endif
00220 
00221 #ifndef _WIN32 // file permissions are too screwy in win32
00222 int wvchmod(const char *path, mode_t mode)
00223 {
00224     struct stat st;
00225     if (lstat(path, &st) == -1) {
00226         return -1;
00227     }
00228 
00229     int filedes = open(path, O_RDONLY);
00230     if (filedes == -1) {
00231         // if we're not running as root, this file/dir may have 0
00232         // perms and open() fails, so let's try again
00233         //
00234         // NOTE: This is not as secure as the proper way, since
00235         // it's conceivable that someone swaps out the dir/file
00236         // for a symlink between our check and the chmod() call
00237         //
00238         struct stat sst;
00239         if (getuid() != 0)
00240             if (stat(path, &sst) != -1)
00241                 if (st.st_ino == sst.st_ino)
00242                     return chmod(path, mode);
00243         
00244         return -1;
00245     }
00246 
00247     struct stat fst;
00248     if (fstat(filedes, &fst) == -1) {
00249         close(filedes);
00250         return -1;
00251     }
00252 
00253     if (st.st_ino != fst.st_ino) {
00254         close(filedes);
00255         return -1;
00256     }
00257 
00258 #ifndef _WIN32
00259     // we're definitely chmod'ing the open file here, which is good,
00260     // because the filename itself might have been moved around between
00261     // our stat'ing and chmod'ing it.
00262     int retval = fchmod(filedes, mode);
00263 #else
00264     // this is guaranteed to be the same file as filedes, because in
00265     // Windows, open files can't be changed on the filesystem (unlike in
00266     // Unix).
00267     int retval = chmod(path, mode);
00268 #endif
00269     close(filedes);
00270 
00271     return retval;
00272 }
00273 #endif // !_WIN32
00274 
00275 
00276 FILE *wvtmpfile()
00277 {
00278 #ifndef _WIN32 // tmpfile() is really the best choice, when it works
00279     return tmpfile();
00280 #else
00281     // in win32, tmpfile() creates files in c:\...
00282     // and that directory isn't always writable!  Idiots.
00283     char *name = _tempnam("c:\\temp", "wvtmp");
00284     FILE *f = fopen(name, "wb+");
00285     free(name);
00286     return f;
00287 #endif
00288 }
00289 
00290 
00291 WvString wvtmpfilename(WvStringParm prefix)
00292 {
00293 #ifndef _WIN32 // tmpfile() is really the best choice, when it works
00294     WvString tmpname("/tmp/%sXXXXXX", prefix);
00295     int fd;
00296     if ((fd = mkstemp(tmpname.edit())) == (-1))
00297         return WvString();    
00298     close(fd);
00299 #else
00300     WvString tmpname(_tempnam("c:\\temp", prefix.cstr()));
00301     int fd;
00302     fd = open(tmpname, O_WRONLY|O_CREAT|O_EXCL, 0777);
00303     if (fd < 0)
00304         return WvString::null; // weird
00305     _close(fd);
00306 #endif
00307 
00308     return tmpname;
00309 }
00310 
00311 
00312 mode_t get_umask()
00313 {
00314     mode_t rv = umask(0);
00315     umask(rv);
00316 
00317     return rv;
00318 }
00319