Main Page | Namespace List | Class Hierarchy | Class List | Directories | File List | Namespace Members | Class Members | File Members

FS.C

Go to the documentation of this file.
00001 // Copyright (C) 2001, Compaq Computer Corporation
00002 
00003 // This file is part of Vesta.
00004 
00005 // Vesta is free software; you can redistribute it and/or
00006 // modify it under the terms of the GNU Lesser General Public
00007 // License as published by the Free Software Foundation; either
00008 // version 2.1 of the License, or (at your option) any later version.
00009 
00010 // Vesta is distributed in the hope that it will be useful,
00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013 // Lesser General Public License for more details.
00014 
00015 // You should have received a copy of the GNU Lesser General Public
00016 // License along with Vesta; if not, write to the Free Software
00017 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018 
00019 #include <errno.h>    // defines ENOENT error code
00020 #include <fcntl.h>    // defines O_RDONLY, O_WRONLY, etc.
00021 #include <string.h>   // for strerror(3)
00022 #include <sys/stat.h> // for stat(2)
00023 #include <unistd.h>   // for unlink(2), rmdir(2), etc.
00024 #include <limits.h>
00025 #include <Basics.H>
00026 #include "FS.H"
00027 
00028 using std::ios;
00029 
00030 static void PrintBanner(std::ostream &os, char c, int num) throw ()
00031 {
00032     os << '\n';
00033     for (int i = 0; i < num; i++) os << c;
00034     os << '\n';
00035 }
00036 
00037 std::ostream& operator<<(std::ostream &os, const FS::Failure &f) throw ()
00038 {
00039     PrintBanner(os, '*', 60);
00040     os << "FS::Failure:\n";
00041     os << "  opName = " << f.get_op() << "\n";
00042     os << "  arg(s) = " << f.get_arg() << "\n";
00043     os << "  errno  = " << f.get_errno();
00044     Text errmsg = Basics::errno_Text(f.get_errno());
00045     if (!errmsg.Empty()) os << " (" << errmsg << ")";
00046     PrintBanner(os, '*', 60);
00047     os.flush();
00048     return os;
00049 }
00050 
00051 void FS::OpenReadOnly(const Text &fname, /*OUT*/ std::ifstream &ifs)
00052   throw (FS::DoesNotExist, FS::Failure)
00053 /* REQUIRES Unopened(ifs) */
00054 {
00055   int errno_save;
00056   while(1)
00057     {
00058       ifs.open(fname.chars(), ios::in);
00059       errno_save = errno;
00060 
00061       // Exit the loop if the file was opened successfully, or if the
00062       // error wasn't EINTR.
00063       if(ifs.good() || (errno_save != EINTR))
00064         break;
00065 
00066       // Clear the stream state before we try again, as open may not
00067       // reset it to good for us.
00068       ifs.clear(ios::goodbit);
00069     }
00070 
00071   if(!ifs.good())
00072     {
00073       // The file could not be opened for reading; in this case, "errno"
00074       // is ENOENT if the file didn't exist (see open(2)).
00075       if(errno_save == ENOENT)
00076         throw FS::DoesNotExist();
00077       else
00078         throw FS::Failure(Text("FS::OpenReadOnly"), fname, errno_save);
00079     }
00080   assert(ifs.good());
00081 }
00082 
00083 void FS::OpenForWriting(const Text &fname, /*OUT*/ std::ofstream &ofs)
00084   throw (FS::Failure)
00085 /* REQUIRES Unopened(ofs) */
00086 {
00087   int errno_save;
00088   // Normal case: just use the ofstream open method.
00089   while(1)
00090     {
00091       ofs.open(fname.chars(), ios::out|ios::trunc);
00092       errno_save = errno;
00093 
00094       // Exit the loop if the file was opened successfully, or if
00095       // the error wasn't EINTR.
00096       if(ofs.good() || (errno_save != EINTR))
00097         break;
00098 
00099       // Clear the stream state before we try again, as open may
00100       // not reset it to good for us.
00101       ofs.clear(ios::goodbit);
00102     }
00103           
00104   if(!ofs.good())
00105     {
00106       throw FS::Failure(Text("FS::OpenForWriting [ofstream::open]"),
00107                         fname, errno_save);
00108     }
00109 }
00110 
00111 void FS::Seek(std::istream &ifs, std::streamoff offset, ios::seekdir orig)
00112   throw (FS::Failure)
00113 {
00114   if(!(ifs.seekg(offset, orig)))
00115     {
00116       int errno_save = errno;
00117       std::ostringstream buff;
00118       buff << "offset = " << offset << ", orig = " << orig;
00119       throw FS::Failure(Text("FS::Seek(std::istream) [istream::seekg]"), Text(buff.str()),
00120                         errno_save);
00121     }
00122 
00123   // In some (broken?) implementations, ifs.sync() is needed to make
00124   // subsequent stream operations correct.  (See OSF_QAR #73434 filed
00125   // by Allan Heydon on 7/21/99.)
00126 
00127   if(ifs.sync() == EOF)
00128     {
00129       int errno_save = errno;
00130       std::ostringstream buff;
00131       buff << "offset = " << offset << ", orig = " << orig;
00132       throw FS::Failure(Text("FS::Seek(std::istream) [istream::sync]"), Text(buff.str()),
00133                         errno_save);
00134     }
00135 }
00136 
00137 void FS::Seek(std::ostream &ofs, std::streamoff offset, ios::seekdir orig)
00138   throw (FS::Failure)
00139 {
00140     if (!(ofs.seekp(offset, orig))) {
00141       int errno_save = errno;
00142       std::ostringstream buff;
00143       buff << "offset = " << offset << ", orig = " << orig;
00144       throw FS::Failure(Text("FS::Seek(std::ostream)"), Text(buff.str()),
00145                         errno_save);
00146     }
00147 }
00148 
00149 std::streampos FS::Posn(std::istream &ifs) throw (FS::Failure)
00150 {
00151     std::streampos res = ifs.tellg();
00152     if (!ifs) throw FS::Failure(Text("FS::Posn(std::istream)"));
00153     return res;
00154 }
00155 
00156 std::streampos FS::Posn(std::ostream &ofs) throw (FS::Failure)
00157 {
00158     std::streampos res = ofs.tellp();
00159     if (!ofs) throw FS::Failure(Text("FS::Posn(std::ostream)"));
00160     return res;
00161 }
00162 
00163 void FS::Read(std::istream &ifs, char *buff, int n)
00164   throw (FS::EndOfFile, FS::Failure)
00165 {
00166     if (!(ifs.read(buff, n))) {
00167       int errno_save = errno;
00168       if (ifs.gcount() < n) throw FS::EndOfFile();
00169       else {
00170         std::ostringstream buff;
00171         buff << "count = " << n;
00172         throw FS::Failure(Text("FS::Read"), Text(buff.str()),
00173                           errno_save);
00174       }
00175     }
00176 }
00177 
00178 void FS::Write(std::ostream &ofs, char *buff, int n) throw (FS::Failure)
00179 {
00180     if (!(ofs.write(buff, n))) {
00181       int errno_save = errno;
00182       std::ostringstream buff;
00183       buff << "count = " << n;
00184       throw FS::Failure(Text("FS::Write"), Text(buff.str()),
00185                         errno_save);
00186     }
00187 }
00188 
00189 void FS::Copy(std::istream &ifs, std::ostream &ofs, int len)
00190   throw (FS::EndOfFile, FS::Failure)
00191 {
00192     const int BuffSz = 1024;
00193     char buff[BuffSz];
00194     while (len > 0) {
00195         int toCopy = min(len, BuffSz);
00196         FS::Read(ifs, buff, toCopy);
00197         FS::Write(ofs, buff, toCopy);
00198         len -= toCopy;
00199     }
00200 }
00201 
00202 bool FS::AtEOF(std::istream &is) throw ()
00203 {
00204     if (is.eof()) return true;
00205     if (is.rdstate() != 0) assert(false);
00206     return (is.peek() == EOF);
00207 }
00208 
00209 void FS::Flush(std::ostream &ofs) throw (FS::Failure)
00210 {
00211   // Check for errors both before and after the flush.  Note that in
00212   // this case we use good(), which will include the EOF bit, as some
00213   // C++ runtime libraries use that bit to represent failed writes
00214   // when the disk is full.
00215   if(!ofs.good())
00216      throw (FS::Failure(Text("FS::Flush(std::ostream) [on entry]")));
00217   ofs.flush();
00218   if(!ofs.good())
00219      throw (FS::Failure(Text("FS::Flush(std::ostream) [after flush]")));
00220 }
00221 
00222 void FS::Close(std::ifstream &ifs) throw (FS::Failure)
00223 {
00224   // Some C++ runtime libraries will clear error bits after closing a
00225   // stream, so we check for errors before and after closing.  Note
00226   // that for input streams, we only test badbit, because eofbit and (at
00227   // least on some libraries) failbit will be set in the normal case of
00228   // failing to read beyond the end of file.
00229   if(ifs.bad())
00230     throw (FS::Failure(Text("FS::Close(std::ifstream) [on entry]")));
00231   ifs.close();
00232   if (ifs.bad())
00233     throw (FS::Failure(Text("FS::Close(std::ifstream) [after close]")));
00234 }
00235 
00236 void FS::Close(std::ofstream &ofs) throw (FS::Failure)
00237 {
00238   // Some C++ runtime libraries will clear error bits after closing a
00239   // stream, and there could be buffered writes pending, so we check
00240   // for problems at three points: on entry, after flushing any
00241   // pending writes, and after closing the stream.  Note that in this
00242   // case we use good(), which will include the EOF bit, as some C++
00243   // runtime libraries use that bit to represent failed writes when
00244   // the disk is full.
00245   if(!ofs.good())
00246      throw (FS::Failure(Text("FS::Close(std::ofstream) [on entry]")));
00247   ofs.flush();
00248   if(!ofs.good())
00249      throw (FS::Failure(Text("FS::Close(std::ofstream) [after flush]")));
00250   ofs.close();
00251   if(!ofs.good())
00252      throw (FS::Failure(Text("FS::Close(std::ofstream) [after close]")));
00253 }
00254 
00255 void FS::Close(std::fstream &fs) throw (FS::Failure)
00256 {
00257   // Some C++ runtime libraries will clear error bits after closing a
00258   // stream, and there could be buffered writes pending.  However, in
00259   // the case of an std::fstream, it's not clear what the correct
00260   // behavior should be, since it could represent either or both input
00261   // and ouptput.
00262 
00263   // As with std::ifstream, we use the bad() operator, which ignores eofbit
00264   // and failbit, because this could be an input file stream that has
00265   // reached the end of file.  However, this could miss some real
00266   // error cases, as some C++ runtime libraries eofbit to represent
00267   // failed writes when the disk is full.
00268 
00269   if(fs.bad())
00270     throw (FS::Failure(Text("FS::Close(std::fstream) [on entry]")));
00271   // We also flush pending writes before closing, in case those write
00272   // fail.  (This should be safe for input streams.)
00273   fs.flush();
00274   if(fs.bad())
00275      throw (FS::Failure(Text("FS::Close(std::fstream) [after flush]")));
00276   fs.close();
00277   if (fs.bad())
00278     throw (FS::Failure(Text("FS::Close(std::fstream) [after close]")));
00279 }
00280 
00281 void FS::Delete(const Text &fname) throw (FS::Failure)
00282 {
00283   bool fail;
00284   do
00285     {
00286       fail = (unlink(fname.cchars()) != 0);
00287     }
00288   while(fail && (errno == EINTR));
00289 
00290   if(fail)
00291     {
00292       throw (FS::Failure(Text("FS::Delete"), fname));
00293     }
00294 }
00295 
00296 bool FS::DeleteDir(const Text &dirname) throw (FS::Failure)
00297 {
00298   bool fail;
00299   // According to the Single UNIX Speciciation, rmdir(2) should never
00300   // fail with EINTR and yet unlink(2) is allowed to fail with EINTR.
00301   // It therefore seems prudent to handle it here.
00302   do
00303     { 
00304       fail = (rmdir(dirname.cchars()) < 0);
00305     }
00306   while(fail && (errno == EINTR));
00307 
00308   // For "directory not empty" some platforms use EEXIST and some use
00309   // ENOTEMPTY.  We just accept either.
00310   if (fail && (errno != EEXIST) && (errno != ENOTEMPTY))
00311     {
00312       throw (FS::Failure(Text("FS::DeleteDir"), dirname));
00313     }
00314 
00315   return !fail;
00316 }
00317 
00318 bool FS::Exists(const Text &fname) throw ()
00319 {
00320   struct stat l_stat_buf;
00321   // Note: stat(2) is not allowed to fail with EINTR.
00322   bool fail = (stat(fname.cchars(), &l_stat_buf) != 0);
00323   return !fail;
00324 }
00325 
00326 bool FS::IsFile(const Text &fname) throw ()
00327 {
00328   struct stat l_stat_buf;
00329   // Note: stat(2) is not allowed to fail with EINTR.
00330   bool fail = (stat(fname.cchars(), &l_stat_buf) != 0);
00331   return (!fail && S_ISREG(l_stat_buf.st_mode));
00332 }
00333 
00334 bool FS::IsDirectory(const Text &fname) throw ()
00335 {
00336   struct stat l_stat_buf;
00337   // Note: stat(2) is not allowed to fail with EINTR.
00338   bool fail = (stat(fname.cchars(), &l_stat_buf) != 0);
00339   return (!fail && S_ISDIR(l_stat_buf.st_mode));
00340 }
00341 
00342 void FS::Touch(const Text &fname, mode_t mode, bool allowOverwrite)
00343   throw (FS::AlreadyExists, FS::Failure)
00344 {
00345   int oflag = O_CREAT|O_WRONLY;
00346   oflag |= allowOverwrite ? O_TRUNC : O_EXCL;
00347   int fd;
00348   int errno_save;
00349   do
00350     {
00351       fd = ::open(fname.cchars(), oflag, mode);
00352       errno_save = errno;
00353     }
00354   // Exit the loop if the file was opened successfully, or if the
00355   // error wasn't EINTR.
00356   while((fd < 0) && (errno == EINTR));
00357   if (fd < 0) {
00358     if (errno_save == EEXIST) {
00359       assert(!allowOverwrite);
00360       throw FS::AlreadyExists();
00361     } else {
00362       throw FS::Failure(Text("FS::Touch [open(2)]"), fname, errno_save);
00363     }
00364   }
00365   int close_res;
00366   do
00367     close_res = ::close(fd);
00368   while((close_res != 0) && (errno == EINTR));
00369   if(close_res != 0)
00370     throw FS::Failure(Text("FS::Touch [close(2)]"), fname);
00371 }
00372 
00373 
00374 Text FS::ParentDir(const Text &fname) throw (FS::Failure)
00375 {
00376   Text name, parent;
00377   SplitPath(fname, name, parent);
00378   return parent;
00379 }
00380 
00381 
00382 Text FS::GetAbsolutePath(const Text &name) throw(FS::Failure)
00383 {
00384   Text apath = name;
00385   if(name.Empty() || name[0] != '/') {
00386     char cwdbuf[PATH_MAX+1];
00387     char *cwd = getcwd(cwdbuf, sizeof(cwdbuf));
00388     if (cwd == NULL) {
00389         throw FS::Failure(Text("FS::GetAbsolutePath [getcwd]"), name, errno);
00390     }
00391     if (name.Empty()) {
00392       apath = Text(cwd);
00393     } 
00394     else {
00395       apath = Text(cwd) + "/" + name;
00396     }
00397   }
00398   return apath;
00399 }
00400 
00401 Text FS::RemoveSpecialArcs(const Text &name) throw()
00402 {
00403   int len = name.Length();
00404   int start = 1;
00405   Text cname = "";
00406   while (start < len) {
00407     int slash = name.FindChar('/', start);
00408     if (slash == -1) 
00409       slash = len;
00410     Text comp = name.Sub(start, slash - start);
00411     if (comp == "." || comp == "") {
00412       // skip
00413     } 
00414     else if (comp == "..") {
00415       // delete previous component
00416       int prev = cname.FindCharR('/');
00417       if(prev > -1)
00418         cname = cname.Sub(0, prev);
00419       else
00420         cname = "";
00421     } 
00422     else {
00423       // keep
00424       cname = cname + "/" + comp;
00425     }
00426     start = slash + 1;
00427   }
00428   if(cname == "")
00429     cname = "/";
00430   return cname;
00431 }
00432 
00433 void FS::SplitPath(const Text &fname, 
00434                    /*OUT*/ Text &name, /*OUT*/ Text &parent) throw(FS::Failure)
00435 {
00436   Text apath = GetAbsolutePath(fname);
00437   name = RemoveSpecialArcs(apath);
00438 
00439   int last_slash_pos = name.FindCharR(PathnameSep);
00440 
00441   while(last_slash_pos != -1) {
00442     if(last_slash_pos + 1 < name.Length()) {
00443       // get everything after last slash for the name and 
00444       // before the last slash for the path
00445       if(last_slash_pos == 0)
00446         parent = "/";
00447       else
00448         parent = name.Sub(0, last_slash_pos);
00449       name = name.Sub(last_slash_pos + 1);  
00450       break;
00451     }
00452     else {
00453       // there is nothing after last slash.
00454       if(last_slash_pos) {
00455         // get path till the last slash and work on it.
00456         name = name.Sub(0, last_slash_pos);
00457         last_slash_pos = name.FindCharR(PathnameSep);
00458       }
00459       else { // fname is something like one slash ore more.
00460         name = "/";
00461         parent = "/";
00462         break;
00463       }
00464     }
00465   }
00466 }
00467 
00468 Text FS::TempFname(Text prefix, Text (*suffix_func)(), mode_t mode)
00469   throw (FS::Failure)
00470 {
00471   int oflag = O_CREAT|O_WRONLY|O_EXCL;
00472   Text fname;
00473   int fd;
00474   int errno_save;
00475   while(1)
00476     {
00477       // Generate a candidate name;
00478       fname = prefix+suffix_func();
00479       fd = ::open(fname.cchars(), oflag, mode);
00480       errno_save = errno;
00481       // If this filename already exists, just try again with a new
00482       // name.
00483       if((fd == -1) && (errno_save == EEXIST))
00484         continue;
00485       // If we succeeded in creating the file, or if there was an
00486       // error other than EINTR, exit the loop.
00487       else if((fd != -1) || (errno_save != EINTR))
00488         break;
00489     }
00490   // If we encountered an error while trying to create the file, throw
00491   // an exception.
00492   if (fd < 0)
00493     {
00494       assert(errno_save != EEXIST);
00495       throw FS::Failure(Text("FS::TempFname [open(2)]"), fname, errno_save);
00496     }
00497   // Close the file descriptor we opened.
00498   int close_res;
00499   do
00500     close_res = ::close(fd);
00501   while((close_res != 0) && (errno == EINTR));
00502   if(close_res != 0)
00503     throw FS::Failure(Text("FS::TempFname [close(2)]"), fname);
00504   // Return the name of the temporary file we created.
00505   return fname;
00506 }

Generated on Mon May 8 00:48:30 2006 for Vesta by  doxygen 1.4.2