file.cpp

Go to the documentation of this file.
00001 /*
00002  * file.cpp
00003  *
00004  * Copyright (c) 2003-2009  Thomas A. Vaughan
00005  * All rights reserved.
00006  *
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted provided that the following conditions are met:
00010  *     * Redistributions of source code must retain the above copyright
00011  *       notice, this list of conditions and the following disclaimer.
00012  *     * Redistributions in binary form must reproduce the above copyright
00013  *       notice, this list of conditions and the following disclaimer in the
00014  *       documentation and/or other materials provided with the distribution.
00015  *     * Neither the name of the <organization> nor the
00016  *       names of its contributors may be used to endorse or promote products
00017  *       derived from this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THOMAS A. VAUGHAN ''AS IS'' AND ANY
00020  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00021  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00022  * DISCLAIMED. IN NO EVENT SHALL THOMAS A. VAUGHAN BE LIABLE FOR ANY
00023  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00024  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00025  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00026  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00027  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00028  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029  *
00030  *
00031  * Helpful file/filename utilities.  See file.h
00032  */
00033 
00034 // includes --------------------------------------------------------------------
00035 #include "file.h"       // always include our own header first
00036 #include "common/wave_ex.h"
00037 
00038 #include <ctype.h>
00039 #include <dirent.h>
00040 #include <pthread.h>
00041 #include <sstream>
00042 
00043 
00044 
00045 static const char s_separator =
00046 #ifdef WIN32
00047         '\\';
00048 #else
00049         '/';
00050 #endif  // WIN32
00051 
00052 
00053 ////////////////////////////////////////////////////////////////////////////////
00054 //
00055 //      Public APIs
00056 //
00057 ////////////////////////////////////////////////////////////////////////////////
00058 
00059 const char *
00060 GetExtension
00061 (
00062 IN const char * filename
00063 )
00064 throw()
00065 {
00066         const char * p = filename;
00067         if (!p)
00068                 return p;
00069 
00070         // go to end of string
00071         while (*p) {
00072                 p++;
00073         }
00074 
00075         // back up until we hit a period
00076         while (*p != '.' && p > filename) {
00077                 p--;
00078         }
00079 
00080         if (*p == '.') {
00081                 return p + 1;
00082         }
00083 
00084         ASSERT(p == filename, "Should be at beginning of string");
00085         return NULL;
00086 }
00087 
00088 
00089 
00090 bool
00091 hasExtension
00092 (
00093 IN const char * filename,
00094 IN const char * extension
00095 )
00096 throw()
00097 {
00098         ASSERT(filename, "null");
00099         ASSERT(extension, "null");
00100 
00101         const char * ext = GetExtension(filename);
00102         if (!ext)
00103                 return false;   // no extension in filename
00104 
00105         return !strcasecmp(ext, extension);
00106 }
00107 
00108 
00109 
00110 void
00111 GetFileRoot
00112 (
00113 IN const char * filename,
00114 OUT std::string& root_name
00115 )
00116 {
00117         ASSERT(filename, "NULL filename");
00118 
00119         const char * start = NULL;
00120         const char * end = NULL;
00121         const char * p = filename;
00122 
00123         // go to the end of the string
00124         while (*p) {
00125                 p++;
00126         }
00127 
00128         // back up, looking for slash or dot
00129         while (!start) {
00130                 p--;
00131                 if (p < filename)
00132                         start = filename;
00133 
00134                 if (!end && '.' == *p)
00135                         end = p;
00136                 else if (s_separator == *p)
00137                         start = p + 1;
00138         }
00139 
00140         ASSERT(start, "never found a root filename: %s", filename);
00141 
00142         // if end!=null, it points to the last period
00143         // if start!=null, it points to the first root filename character
00144 
00145         root_name = "";
00146         while (*start && start != end) {
00147                 root_name += *start;
00148                 ++start;
00149         }
00150 }
00151 
00152 
00153 void
00154 GetParentDirectory
00155 (
00156 IN const char * filename,
00157 OUT std::string& directory
00158 )
00159 throw()
00160 {
00161         ASSERT(filename, "NULL filename");
00162         ASSERT(*filename, "empty file");
00163         directory.clear();
00164 
00165         const char * p = filename;
00166 
00167         // go to end
00168         while (*p) {
00169                 p++;
00170         }
00171 
00172         // back up, look for first separator
00173         while (p > filename && *p != s_separator) {
00174                 --p;
00175         }
00176 
00177         if (s_separator != *p) {
00178                 // no separators at all!
00179                 return;
00180         }
00181 
00182         if (p == filename) {
00183                 // this is the root!
00184                 directory = s_separator;
00185                 return;
00186         }
00187 
00188         // have an interesting parent path (not root or empty)
00189         while (filename < p) {
00190                 directory += *filename;
00191                 filename++;
00192         }
00193 }
00194 
00195 
00196 
00197 const char *
00198 getTopDirectory
00199 (
00200 IN const char * filename,
00201 OUT std::string& topDir
00202 )
00203 {
00204         ASSERT(filename, "null");
00205         topDir.clear();
00206 
00207         // look for first slash
00208         const char * p = filename;
00209         while (*p && *p != s_separator) {
00210                 topDir += *p;
00211                 ++p;
00212 
00213         }
00214         if (!*p) {
00215                 // no slashes!
00216                 topDir.clear();
00217                 return filename;
00218         }
00219 
00220         return p + 1;
00221 }
00222 
00223 
00224 
00225 const char *
00226 GetFilename
00227 (
00228 IN const char * path
00229 )
00230 throw()
00231 {
00232         const char * p = path;
00233 
00234         ASSERT(path, "NULL path");
00235         ASSERT(*path, "empty path");
00236 
00237         // go to end
00238         while (*p) {
00239                 p++;
00240         }
00241 
00242         // back up to first separator
00243         while (p > path && *p != s_separator) {
00244                 --p;
00245         }
00246 
00247         // walk forward if on separator
00248         if (s_separator == *p)
00249                 p++;
00250 
00251         // this is it
00252         return p;
00253 }
00254 
00255 
00256 
00257 void
00258 getTempfile
00259 (
00260 IN const char * filename,
00261 OUT std::string& tempfile
00262 )
00263 throw()
00264 {
00265         ASSERT(filename, "null");
00266 
00267         // get the directory
00268         std::string directory;
00269         GetParentDirectory(filename, directory);
00270 
00271         int x = rand() % 10000;
00272 
00273         // convoluted casting to get a process ID
00274         // note that this is broken!  For all I know, the
00275         // first 4 bytes of a pthread aren't unique at all!
00276         pthread_t thread_id = pthread_self();
00277         long pid = *(long *)(&thread_id);
00278 
00279         // create a temp filename (prepend a tilde, append process ID, random x, and .tmp)
00280         std::ostringstream temp;
00281         temp << directory << "~" << GetFilename(filename);
00282         temp << "." << pid << "-" << x << ".tmp";
00283         tempfile = temp.str();
00284 
00285         // DPRINTF("tempfile: %s", tempfile.c_str());
00286 }
00287 
00288 
00289 
00290 void
00291 getUserHomePath
00292 (
00293 OUT std::string& path
00294 )
00295 {
00296 #ifdef WIN32
00297         path = "c:/Users/";
00298 #else   // WIN32
00299         // initialize
00300         path = "/home/";
00301 
00302         // attempt to determine username
00303         const char * login = getenv("LOGNAME"); // prefer environment variable
00304         if (!login)
00305                 login = getlogin();             // fallback is controlling terminal
00306 
00307         // if we have a username, append it
00308         if (login)
00309                 path += login;
00310 #endif  // WIN32
00311 }
00312 
00313 
00314 
00315 bool
00316 ContainsWhitespace
00317 (
00318 IN const char * test
00319 )
00320 throw()
00321 {
00322         if (!test)
00323                 return false;   // edge case
00324 
00325         const char * p = test;
00326         while (*p) {
00327                 if (isspace(*p))
00328                         return true;
00329                 ++p;
00330         }
00331         return false;
00332 }
00333 
00334 
00335 
00336 /*
00337 error_t
00338 appendLogfile
00339 (
00340 IN const char * filename,
00341 IN const char * logline
00342 )
00343 throw()
00344 {
00345         ASSERT(filename, "NULL filename");
00346         ASSERT(logline, "NULL logline");
00347 
00348         FILE * file = fopen(filename, "a+");
00349         if (!file) {
00350                 error_t error = errno;
00351                 DPRINTF("Failed to open logfile %s : %d (%s)", filename,
00352                     error, strerror(error));
00353                 return error;
00354         }
00355         fprintf(file, "%s\n", logline);
00356         fclose(file);
00357         return 0;
00358 }
00359 */
00360 
00361 
00362 #ifdef WIN32
00363 #include <sys/stat.h>
00364 #define stat(path, ptr) _stat(path, ptr)
00365 typedef struct _stat stat_t;
00366 #else   // WIN32
00367 typedef struct stat stat_t;
00368 #endif  // WIN32
00369 
00370 
00371 bool
00372 doesPathExist
00373 (
00374 IN const char * path
00375 )
00376 throw()
00377 {
00378         ASSERT(path, "null");
00379 
00380         stat_t buf;
00381         int retval = stat(path, &buf);
00382         //DPRINTF("stat('%s') = %d", path, retval);
00383         //DPRINTF("  st_dev = 0x%08lx", (dword_t) buf.st_dev);
00384         //DPRINTF("  st_ino = 0x%08lx", (dword_t) buf.st_ino);
00385         if (-1 == retval) {
00386                 return false;           // does not exist!
00387         }
00388         return true;
00389 }
00390 
00391 
00392 
00393 void
00394 createEmptyFileIfDoesNotExist
00395 (
00396 IN const char * path
00397 )
00398 throw()
00399 {
00400         ASSERT(path, "null");
00401 
00402         if (doesPathExist(path))
00403                 return;         // nothing to do!
00404         // does not exist, create it
00405         //DPRINTF("Attempting to create empty file '%s'", path);
00406         std::string cmd = "/bin/touch ";
00407         cmd += path;
00408         system(cmd.c_str());
00409 }
00410 
00411 
00412 
00413 bool
00414 matchesExtension
00415 (
00416 IN const char * filename,
00417 IN const char * extension
00418 )
00419 throw()
00420 {
00421         ASSERT(filename, "null");
00422         // ASSERT(extension) -- can be null!
00423 
00424         if (!extension)
00425                 return true;    // NULL extension means "match everything"
00426 
00427         // do last characters of filename match extension?
00428         int extLen = strlen(extension);
00429         int fileLen = strlen(filename);
00430 
00431         int delta = fileLen - extLen;
00432         if (delta <= 0)
00433                 return false;   // filename is too short!
00434 
00435         const char * fileExt = filename + delta;
00436         return !strcasecmp(fileExt, extension);
00437 }
00438 
00439 
00440 
00441 void
00442 walkDirectoryTree
00443 (
00444 IN const char * rootDir,
00445 IN const char * matchExt,
00446 OUT VecString& paths
00447 )
00448 {
00449         ASSERT(rootDir, "null");
00450         // ASSERT(matchExt) -- can be null!
00451         paths.clear();
00452 
00453 //      DPRINTF("Walking directory: %s", rootDir);
00454 
00455         SetString all;
00456 
00457         // first walk directory, save files, and remember directories
00458         DIR * dir = opendir(rootDir);
00459         THROW(dir , "Failed to open directory: " << rootDir);
00460         while (true) {
00461                 dirent * entry = readdir(dir);
00462                 if (!entry) {
00463 //                      DPRINTF("  end of directory!");
00464                         break;
00465                 }
00466                 if ('.' == entry->d_name[0]) {
00467                         continue;       // skip all paths beginning with '.'
00468                 }
00469                 all.insert(entry->d_name);
00470         }
00471         closedir(dir);
00472 
00473         // now walk everything (in lexigraphical order) and investigate
00474         VecString children;
00475         for (SetString::iterator i = all.begin(); i != all.end(); ++i) {
00476                 const char * name = i->c_str();
00477 
00478                 std::string full_path = rootDir;
00479                 full_path += "/";
00480                 full_path += name;
00481 
00482 //              DPRINTF("  Examining %s", full_path.c_str());
00483 
00484                 // is this a directory?
00485                 stat_t sb;
00486                 if (!stat(full_path.c_str(), &sb)) {
00487                         if (S_IFDIR & sb.st_mode) {
00488                                 // this is a directory!
00489                                 children.push_back(name);
00490                         } else if (matchesExtension(name, matchExt)) {
00491                                 // this is a file
00492 //                              DPRINTF("    File!  %s", entry->d_name);
00493                                 paths.push_back(name);
00494                         }
00495                 }
00496         }
00497 
00498         // now walk all child directories
00499         for (VecString::const_iterator i = children.begin();
00500              i != children.end(); ++i) {
00501                 const char * name = i->c_str();
00502 
00503                 std::string newRoot = rootDir;
00504                 newRoot += "/";
00505                 newRoot += name;
00506 
00507                 VecString subEntries;
00508                 walkDirectoryTree(newRoot.c_str(), matchExt, subEntries);
00509 
00510                 // update subEntries
00511                 for (VecString::const_iterator j = subEntries.begin();
00512                      j != subEntries.end(); ++j) {
00513                         const char * subName = j->c_str();
00514 
00515                         std::string relPath = name;
00516                         relPath += "/";
00517                         relPath += subName;
00518 
00519 //                      DPRINTF("  Adding sub-entry: %s", relPath.c_str());
00520                         paths.push_back(relPath.c_str());
00521                 }
00522         }
00523 }
00524 
00525 
00526 
00527 std::string
00528 getPathRelativeTo
00529 (
00530 IN const char * startFile,
00531 IN const char * relPath
00532 )
00533 {
00534         ASSERT(startFile, "null");
00535         ASSERT(relPath, "null");
00536 
00537         std::string path;
00538         if (*startFile) {
00539                 GetParentDirectory(startFile, path);
00540         }
00541         if (path.length() > 0) {
00542                 path += "/";
00543         }
00544         path += relPath;
00545 
00546         return path;
00547 }
00548 
00549 
00550 
00551 void
00552 appendPath
00553 (
00554 IN const char * parentDirectory,
00555 IN const char * nextPath,
00556 OUT std::string& path
00557 )
00558 {
00559         ASSERT(parentDirectory, "null");
00560         ASSERT(nextPath, "null");
00561 
00562         path = parentDirectory;
00563 
00564         // only append if necessary!
00565         int len = strlen(parentDirectory);
00566         if (len > 0 &&
00567             s_separator != parentDirectory[len - 1] &&
00568             nextPath[0] &&
00569             s_separator != nextPath[0]) {
00570                 path += s_separator;
00571         }
00572         path += nextPath;
00573 }
00574