1// This file is part of the ustl library, an STL implementation. 2// 3// Copyright (C) 2005 by Mike Sharov <msharov@users.sourceforge.net> 4// This file is free software, distributed under the MIT License. 5// 6// file.cc 7// 8 9#include "fstream.h" 10#include "uassert.h" 11#include "uexception.h" 12#include "uutility.h" 13 14#include <fcntl.h> 15#include <unistd.h> 16#include <errno.h> 17#include <sys/stat.h> 18#include <sys/mman.h> 19#include <sys/ioctl.h> 20 21#if PLATFORM_ANDROID 22#include <stdio.h> 23#endif 24 25namespace ustl { 26 27/// Default constructor. 28fstream::fstream (void) 29: ios_base (), 30 m_fd (-1), 31 m_Filename () 32{ 33} 34 35/// Opens \p filename in \p mode. 36fstream::fstream (const char* filename, openmode mode) 37: ios_base (), 38 m_fd (-1), 39 m_Filename () 40{ 41 open (filename, mode); 42} 43 44/// Attaches to \p nfd of \p filename. 45fstream::fstream (int nfd, const char* filename) 46: ios_base (), 47 m_fd (-1), 48 m_Filename () 49{ 50 attach (nfd, filename); 51} 52 53/// Destructor. Closes if still open, but without throwing. 54fstream::~fstream (void) throw() 55{ 56 clear (goodbit); 57 exceptions (goodbit); 58 close(); 59 assert (!(rdstate() & badbit) && "close failed in the destructor! This may lead to loss of user data. Please call close() manually and either enable exceptions or check the badbit."); 60} 61 62/// Sets state \p s and throws depending on the exception setting. 63void fstream::set_and_throw (iostate s, const char* op) 64{ 65 if (ios_base::set_and_throw (s)) 66#if PLATFORM_ANDROID 67 printf("file_exception\n"); 68#else /* !PLATFORM_ANDROID */ 69 throw file_exception (op, name()); 70#endif 71} 72 73/// Attaches to the given \p nfd. 74void fstream::attach (int nfd, const char* filename) 75{ 76 assert (filename && "Don't do that"); 77 clear (goodbit); 78 if (nfd < 0 && ios_base::set_and_throw (badbit)) 79#if PLATFORM_ANDROID 80 printf("file exception\n"); 81#else /* !PLATFORM_ANDROID */ 82 throw file_exception ("open", filename); 83#endif 84 close(); 85 m_fd = nfd; 86 m_Filename = filename; 87} 88 89/// Detaches from the current fd. 90void fstream::detach (void) 91{ 92 m_fd = -1; 93 m_Filename.clear(); 94} 95 96/// Converts openmode bits into libc open flags. 97/*static*/ int fstream::om_to_flags (openmode m) 98{ 99 static const int s_OMFlags [nombits] = { 100 0, // in 101 O_CREAT, // out 102 O_APPEND, // app 103 O_APPEND, // ate 104 0, // binary 105 O_TRUNC, // trunc 106 O_NONBLOCK, // nonblock 107 0, // nocreate 108 O_NOCTTY // noctty 109 }; 110 int flags = (m - 1) & O_ACCMODE; // in and out 111 for (uoff_t i = 0; i < VectorSize(s_OMFlags); ++ i) 112 if (m & (1 << i)) 113 flags |= s_OMFlags[i]; 114 if (m & nocreate) 115 flags &= ~O_CREAT; 116 return (flags); 117} 118 119/// \brief Opens \p filename in the given mode. 120/// \warning The string at \p filename must exist until the object is closed. 121void fstream::open (const char* filename, openmode mode, mode_t perms) 122{ 123 int nfd = ::open (filename, om_to_flags(mode), perms); 124 attach (nfd, filename); 125} 126 127/// Closes the file and throws on error. 128void fstream::close (void) 129{ 130 if (m_fd >= 0 && ::close(m_fd)) 131 set_and_throw (badbit | failbit, "close"); 132 detach(); 133} 134 135/// Moves the current file position to \p n. 136off_t fstream::seek (off_t n, seekdir whence) 137{ 138 off_t p = lseek (m_fd, n, whence); 139 if (p < 0) 140 set_and_throw (failbit, "seek"); 141 return (p); 142} 143 144/// Returns the current file position. 145off_t fstream::pos (void) const 146{ 147 return (lseek (m_fd, 0, SEEK_CUR)); 148} 149 150/// Reads \p n bytes into \p p. 151off_t fstream::read (void* p, off_t n) 152{ 153 off_t br (0); 154 while (br < n && good()) 155 br += readsome (advance (p, br), n - br); 156 return (br); 157} 158 159/// Reads at most \p n bytes into \p p, stopping when it feels like it. 160off_t fstream::readsome (void* p, off_t n) 161{ 162 ssize_t brn; 163 do { brn = ::read (m_fd, p, n); } while (brn < 0 && errno == EINTR); 164 if (brn > 0) 165 return (brn); 166 if (brn < 0 && errno != EAGAIN) 167 set_and_throw (failbit, "read"); 168 if (!brn && ios_base::set_and_throw (eofbit | failbit)) 169#if PLATFORM_ANDROID 170 printf("stream_bounds_exception\n"); 171#else /* !PLATFORM_ANDROID */ 172 throw stream_bounds_exception ("read", name(), pos(), n, 0); 173#endif 174 return (0); 175} 176 177/// Writes \p n bytes from \p p. 178off_t fstream::write (const void* p, off_t n) 179{ 180 off_t btw (n); 181 while (btw) { 182 const off_t bw (n - btw); 183 ssize_t bwn = ::write (m_fd, advance(p,bw), btw); 184 if (bwn > 0) 185 btw -= bwn; 186 else if (!bwn) { 187 if (ios_base::set_and_throw (eofbit | failbit)) 188#if PLATFORM_ANDROID 189 printf("stream_bounds_exception\n"); 190#else /* !PLATFORM_ANDROID */ 191 throw stream_bounds_exception ("write", name(), pos() - bw, n, bw); 192#endif 193 break; 194 } else if (errno != EINTR) { 195 if (errno != EAGAIN) 196 set_and_throw (failbit, "write"); 197 break; 198 } 199 } 200 return (n - btw); 201} 202 203/// Returns the file size. 204off_t fstream::size (void) const 205{ 206 struct stat st; 207 st.st_size = 0; 208 stat (st); 209 return (st.st_size); 210} 211 212/// Synchronizes the file's data and status with the disk. 213void fstream::sync (void) 214{ 215 if (fsync (m_fd)) 216 set_and_throw (failbit, "sync"); 217} 218 219/// Get the stat structure. 220void fstream::stat (struct stat& rs) const 221{ 222 if (fstat (m_fd, &rs)) 223#if PLATFORM_ANDROID 224 printf("file_exception\n"); 225#else 226 throw file_exception ("stat", name()); 227#endif 228} 229 230/// Calls the given ioctl. Use IOCTLID macro to pass in both \p name and \p request. 231int fstream::ioctl (const char* rname, int request, long argument) 232{ 233 int rv = ::ioctl (m_fd, request, argument); 234 if (rv < 0) 235 set_and_throw (failbit, rname); 236 return (rv); 237} 238 239/// Calls the given fcntl. Use FCNTLID macro to pass in both \p name and \p request. 240int fstream::fcntl (const char* rname, int request, long argument) 241{ 242 int rv = ::fcntl (m_fd, request, argument); 243 if (rv < 0) 244 set_and_throw (failbit, rname); 245 return (rv); 246} 247 248/// Memory-maps the file and returns a link to it. 249memlink fstream::mmap (off_t n, off_t offset) 250{ 251 void* result = ::mmap (NULL, n, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd, offset); 252 if (result == MAP_FAILED) 253 set_and_throw (failbit, "mmap"); 254 return (memlink (result, n)); 255} 256 257/// Unmaps a memory-mapped area. 258void fstream::munmap (memlink& l) 259{ 260 if (::munmap (l.data(), l.size())) 261 set_and_throw (failbit, "munmap"); 262 l.unlink(); 263} 264 265/// Synchronizes a memory-mapped area. 266void fstream::msync (memlink& l) 267{ 268 if (::msync (l.data(), l.size(), MS_ASYNC | MS_INVALIDATE)) 269 set_and_throw (failbit, "msync"); 270} 271 272void fstream::set_nonblock (bool v) 273{ 274 int curf = fcntl (FCNTLID (F_GETFL)); 275 if (curf < 0) return; 276 if (v) curf |= O_NONBLOCK; 277 else curf &= ~O_NONBLOCK; 278 fcntl (FCNTLID (F_SETFL), curf); 279} 280 281} // namespace ustl 282 283