file.cpp

Go to the documentation of this file.
00001 /*
00002  * file.cpp
00003  *
00004  * Copyright (C) 2003-2010  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 <sstream>
00040 
00041 
00042 
00043 static const char s_separator =
00044 #ifdef WIN32
00045         '/';
00046 //      '\\';
00047 #else
00048         '/';
00049 #endif  // WIN32
00050 
00051 
00052 ////////////////////////////////////////////////////////////////////////////////
00053 //
00054 //      Public APIs
00055 //
00056 ////////////////////////////////////////////////////////////////////////////////
00057 
00058 const char *
00059 GetExtension
00060 (
00061 IN const char * filename
00062 )
00063 throw()
00064 {
00065         const char * p = filename;
00066         if (!p)
00067                 return p;
00068 
00069         // go to end of string
00070         while (*p) {
00071                 p++;
00072         }
00073 
00074         // back up until we hit a period
00075         while (*p != '.' && p > filename) {
00076                 p--;
00077         }
00078 
00079         if (*p == '.') {
00080                 return p + 1;
00081         }
00082 
00083         ASSERT(p == filename, "Should be at beginning of string");
00084         return NULL;
00085 }
00086 
00087 
00088 
00089 bool
00090 hasExtension
00091 (
00092 IN const char * filename,
00093 IN const char * extension
00094 )
00095 throw()
00096 {
00097         ASSERT(filename, "null");
00098         ASSERT(extension, "null");
00099 
00100         const char * ext = GetExtension(filename);
00101         if (!ext)
00102                 return false;   // no extension in filename
00103 
00104         return !strcasecmp(ext, extension);
00105 }
00106 
00107 
00108 
00109 void
00110 GetFileRoot
00111 (
00112 IN const char * filename,
00113 OUT std::string& root_name
00114 )
00115 {
00116         ASSERT(filename, "NULL filename");
00117 
00118         const char * start = NULL;
00119         const char * end = NULL;
00120         const char * p = filename;
00121 
00122         // go to the end of the string
00123         while (*p) {
00124                 p++;
00125         }
00126 
00127         // back up, looking for slash or dot
00128         while (!start) {
00129                 p--;
00130                 if (p < filename)
00131                         start = filename;
00132 
00133                 if (!end && '.' == *p)
00134                         end = p;
00135                 else if (s_separator == *p)
00136                         start = p + 1;
00137         }
00138 
00139         ASSERT(start, "never found a root filename: %s", filename);
00140 
00141         // if end!=null, it points to the last period
00142         // if start!=null, it points to the first root filename character
00143 
00144         root_name = "";
00145         while (*start && start != end) {
00146                 root_name += *start;
00147                 ++start;
00148         }
00149 }
00150 
00151 
00152 void
00153 GetParentDirectory
00154 (
00155 IN const char * filename,
00156 OUT std::string& directory
00157 )
00158 throw()
00159 {
00160         ASSERT(filename, "NULL filename");
00161         ASSERT(*filename, "empty file");
00162         directory.clear();
00163 
00164         const char * p = filename;
00165 
00166         // go to end
00167         while (*p) {
00168                 p++;
00169         }
00170 
00171         // back up, look for first separator
00172         while (p > filename && *p != s_separator) {
00173                 --p;
00174         }
00175 
00176         if (s_separator != *p) {
00177                 // no separators at all!
00178                 return;
00179         }
00180 
00181         if (p == filename) {
00182                 // this is the root!
00183                 directory = s_separator;
00184                 return;
00185         }
00186 
00187         // have an interesting parent path (not root or empty)
00188         while (filename < p) {
00189                 directory += *filename;
00190                 filename++;
00191         }
00192 }
00193 
00194 
00195 
00196 const char *
00197 getTopDirectory
00198 (
00199 IN const char * filename,
00200 OUT std::string& topDir
00201 )
00202 {
00203         ASSERT(filename, "null");
00204         topDir.clear();
00205 
00206         // look for first slash
00207         const char * p = filename;
00208         while (*p && *p != s_separator) {
00209                 topDir += *p;
00210                 ++p;
00211 
00212         }
00213         if (!*p) {
00214                 // no slashes!
00215                 topDir.clear();
00216                 return filename;
00217         }
00218 
00219         return p + 1;
00220 }
00221 
00222 
00223 
00224 const char *
00225 GetFilename
00226 (
00227 IN const char * path
00228 )
00229 throw()
00230 {
00231         const char * p = path;
00232 
00233         ASSERT(path, "NULL path");
00234         ASSERT(*path, "empty path");
00235 
00236         // go to end
00237         while (*p) {
00238                 p++;
00239         }
00240 
00241         // back up to first separator
00242         while (p > path && *p != s_separator) {
00243                 --p;
00244         }
00245 
00246         // walk forward if on separator
00247         if (s_separator == *p)
00248                 p++;
00249 
00250         // this is it
00251         return p;
00252 }
00253 
00254 
00255 
00256 void
00257 getTempfile
00258 (
00259 IN const char * filename,
00260 OUT std::string& tempfile
00261 )
00262 throw()
00263 {
00264         ASSERT(filename, "null");
00265 
00266         // get the directory
00267         std::string directory;
00268         GetParentDirectory(filename, directory);
00269 
00270         int x = rand() % 10000;
00271 
00272         // convoluted casting to get a process ID
00273         // note that this is broken!  For all I know, the
00274         // first 4 bytes of a pthread aren't unique at all!
00275         dword_t thread_id =
00276 #ifdef WIN32
00277                 GetCurrentThreadId();
00278 #else   // WIN32
00279                 pthread_self();
00280 #endif  // WIN32
00281         long pid = *(long *)(&thread_id);
00282 
00283         // create a temp filename (prepend a tilde, append process ID, random x, and .tmp)
00284         std::ostringstream temp;
00285         temp << directory << "~" << GetFilename(filename);
00286         temp << "." << pid << "-" << x << ".tmp";
00287         tempfile = temp.str();
00288 
00289         // DPRINTF("tempfile: %s", tempfile.c_str());
00290 }
00291 
00292 
00293 
00294 void
00295 getUserHomePath
00296 (
00297 OUT std::string& path
00298 )
00299 {
00300 #ifdef WIN32
00301         path = "c:/Users/";
00302 #else   // WIN32
00303         // initialize
00304         path = "/home/";
00305 
00306         // attempt to determine username
00307         const char * login = getenv("LOGNAME"); // prefer environment variable
00308         if (!login)
00309                 login = getlogin();             // fallback is controlling terminal
00310 
00311         // if we have a username, append it
00312         if (login)
00313                 path += login;
00314 #endif  // WIN32
00315 }
00316 
00317 
00318 
00319 bool
00320 ContainsWhitespace
00321 (
00322 IN const char * test
00323 )
00324 throw()
00325 {
00326         if (!test)
00327                 return false;   // edge case
00328 
00329         const char * p = test;
00330         while (*p) {
00331                 if (isspace(*p))
00332                         return true;
00333                 ++p;
00334         }
00335         return false;
00336 }
00337 
00338 
00339 
00340 /*
00341 error_t
00342 appendLogfile
00343 (
00344 IN const char * filename,
00345 IN const char * logline
00346 )
00347 throw()
00348 {
00349         ASSERT(filename, "NULL filename");
00350         ASSERT(logline, "NULL logline");
00351 
00352         FILE * file = fopen(filename, "a+");
00353         if (!file) {
00354                 error_t error = errno;
00355                 DPRINTF("Failed to open logfile %s : %d (%s)", filename,
00356                     error, strerror(error));
00357                 return error;
00358         }
00359         fprintf(file, "%s\n", logline);
00360         fclose(file);
00361         return 0;
00362 }
00363 */
00364 
00365 
00366 #ifdef WIN32
00367 #include <sys/stat.h>
00368 #define stat(path, ptr) _stat(path, ptr)
00369 typedef struct _stat stat_t;
00370 #else   // WIN32
00371 typedef struct stat stat_t;
00372 #endif  // WIN32
00373 
00374 
00375 bool
00376 doesPathExist
00377 (
00378 IN const char * path
00379 )
00380 throw()
00381 {
00382         ASSERT(path, "null");
00383 
00384         stat_t buf;
00385         int retval = stat(path, &buf);
00386         //DPRINTF("stat('%s') = %d", path, retval);
00387         //DPRINTF("  st_dev = 0x%08lx", (dword_t) buf.st_dev);
00388         //DPRINTF("  st_ino = 0x%08lx", (dword_t) buf.st_ino);
00389         if (-1 == retval) {
00390                 return false;           // does not exist!
00391         }
00392         return true;
00393 }
00394 
00395 
00396 
00397 void
00398 createEmptyFileIfDoesNotExist
00399 (
00400 IN const char * path
00401 )
00402 throw()
00403 {
00404         ASSERT(path, "null");
00405 
00406         if (doesPathExist(path))
00407                 return;         // nothing to do!
00408         // does not exist, create it
00409         //DPRINTF("Attempting to create empty file '%s'", path);
00410         std::string cmd = "/bin/touch ";
00411         cmd += path;
00412         system(cmd.c_str());
00413 }
00414 
00415 
00416 
00417 bool
00418 matchesExtension
00419 (
00420 IN const char * filename,
00421 IN const char * extension
00422 )
00423 throw()
00424 {
00425         ASSERT(filename, "null");
00426         // ASSERT(extension) -- can be null!
00427 
00428         if (!extension)
00429                 return true;    // NULL extension means "match everything"
00430 
00431         // do last characters of filename match extension?
00432         int extLen = strlen(extension);
00433         int fileLen = strlen(filename);
00434 
00435         int delta = fileLen - extLen;
00436         if (delta <= 0)
00437                 return false;   // filename is too short!
00438 
00439         const char * fileExt = filename + delta;
00440         return !strcasecmp(fileExt, extension);
00441 }
00442 
00443 
00444 
00445 void
00446 walkDirectoryTree
00447 (
00448 IN const char * rootDir,
00449 IN const char * matchExt,
00450 OUT VecString& paths
00451 )
00452 {
00453         ASSERT(rootDir, "null");
00454         // ASSERT(matchExt) -- can be null!
00455         paths.clear();
00456 
00457         DPRINTF("Walking directory: %s", rootDir);
00458 
00459         SetString all;
00460 
00461         // first walk directory, save files, and remember directories
00462         DIR * dir = opendir(rootDir);
00463         THROW(dir , "Failed to open directory: " << rootDir);
00464         while (true) {
00465                 dirent * entry = readdir(dir);
00466                 if (!entry) {
00467                         DPRINTF("  end of directory!");
00468                         break;
00469                 }
00470                 if ('.' == entry->d_name[0]) {
00471                         continue;       // skip all paths beginning with '.'
00472                 }
00473                 all.insert(entry->d_name);
00474         }
00475         closedir(dir);
00476 
00477         // now walk everything (in lexigraphical order) and investigate
00478         VecString children;
00479         for (SetString::iterator i = all.begin(); i != all.end(); ++i) {
00480                 const char * name = i->c_str();
00481 
00482                 std::string full_path = rootDir;
00483                 full_path += s_separator;
00484                 full_path += name;
00485 
00486                 DPRINTF("  Examining %s", full_path.c_str());
00487 
00488                 // is this a directory?
00489                 stat_t sb;
00490                 if (!stat(full_path.c_str(), &sb)) {
00491                         if (S_IFDIR & sb.st_mode) {
00492                                 // this is a directory!
00493                                 children.push_back(name);
00494                         } else if (matchesExtension(name, matchExt)) {
00495                                 // this is a file
00496                                 DPRINTF("    File!  %s", name);
00497                                 paths.push_back(name);
00498                         }
00499                 }
00500         }
00501 
00502         // now walk all child directories
00503         for (VecString::const_iterator i = children.begin();
00504              i != children.end(); ++i) {
00505                 const char * name = i->c_str();
00506 
00507                 std::string newRoot = rootDir;
00508                 newRoot += s_separator;
00509                 newRoot += name;
00510 
00511                 VecString subEntries;
00512                 walkDirectoryTree(newRoot.c_str(), matchExt, subEntries);
00513 
00514                 // update subEntries
00515                 for (VecString::const_iterator j = subEntries.begin();
00516                      j != subEntries.end(); ++j) {
00517                         const char * subName = j->c_str();
00518 
00519                         std::string relPath = name;
00520                         relPath += s_separator;
00521                         relPath += subName;
00522 
00523                         DPRINTF("  Adding sub-entry: %s", relPath.c_str());
00524                         paths.push_back(relPath.c_str());
00525                 }
00526         }
00527 }
00528 
00529 
00530 
00531 std::string
00532 getPathRelativeTo
00533 (
00534 IN const char * startFile,
00535 IN const char * relPath
00536 )
00537 {
00538         ASSERT(startFile, "null");
00539         ASSERT(relPath, "null");
00540 
00541         std::string path;
00542         if (*startFile) {
00543                 GetParentDirectory(startFile, path);
00544         }
00545         if (path.length() > 0) {
00546                 path += s_separator;
00547         }
00548         path += relPath;
00549 
00550         return path;
00551 }
00552 
00553 
00554 
00555 void
00556 appendPath
00557 (
00558 IN const char * parentDirectory,
00559 IN const char * nextPath,
00560 OUT std::string& path
00561 )
00562 {
00563         ASSERT(parentDirectory, "null");
00564         ASSERT(nextPath, "null");
00565 
00566         path = parentDirectory;
00567 
00568         // only append if necessary!
00569         int len = strlen(parentDirectory);
00570         if (len > 0 &&
00571             s_separator != parentDirectory[len - 1] &&
00572             nextPath[0] &&
00573             s_separator != nextPath[0]) {
00574                 path += s_separator;
00575         }
00576         path += nextPath;
00577 }
00578 
00579 
00580 
00581 #ifdef WIN32
00582 
00583 DIR *
00584 opendir
00585 (
00586 IN const char * path
00587 )
00588 {
00589         ASSERT(path, "null");
00590 
00591         DIR * pdir = new DIR;
00592         ASSERT(pdir, "out of memory");
00593         pdir->path = path;
00594         ASSERT(!pdir->h, "should be null");
00595 
00596         return pdir;
00597 }
00598 
00599 
00600 
00601 struct dirent *
00602 readdir
00603 (
00604 IO DIR * dir
00605 )
00606 {
00607         ASSERT(dir, "null");
00608 
00609         // DPRINTF("calling readdir()...");
00610         static dirent s_d;
00611         WIN32_FIND_DATA wfd;
00612         if (!dir->h) {
00613                 // no handle yet!  find first file
00614                 std::string search = dir->path;
00615                 search += "\\*";        // wildcard
00616                 dir->h = FindFirstFile(search.c_str(), &wfd);
00617                 if (INVALID_HANDLE_VALUE == dir->h ||
00618                     ERROR_FILE_NOT_FOUND == (long) dir->h) {
00619                         DPRINTF("Failed to find first file: %s",
00620                             search.c_str());
00621                         return NULL;
00622                 }
00623                 strncpy(s_d.d_name, wfd.cFileName, MAX_PATH);
00624                 return &s_d;
00625         } else {
00626                 // have a handle!  find next file
00627                 int retval = FindNextFile(dir->h, &wfd);
00628                 if (!retval || ERROR_NO_MORE_FILES == retval) {
00629                         return NULL;
00630                 }
00631                 strncpy(s_d.d_name, wfd.cFileName, MAX_PATH);
00632                 return &s_d;
00633         }
00634 }
00635 
00636 
00637 
00638 int
00639 closedir
00640 (
00641 IN DIR * dir
00642 )
00643 {
00644         ASSERT(dir, "null");
00645 
00646         if (dir->h) {
00647                 FindClose(dir->h);
00648         }
00649         delete dir;
00650         return 0;
00651 }
00652 
00653 
00654 
00655 #endif  // WIN32