/* PIP - Platform Independent Primitives File Ivan Pelipenko peri4ko@yandex.ru This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #include "piincludes_p.h" #include "pifile.h" #include "pidir.h" #include "pitime_win.h" #ifdef WINDOWS # undef S_IFDIR # undef S_IFREG # undef S_IFLNK # undef S_IFBLK # undef S_IFCHR # undef S_IFSOCK # define S_IFDIR 0x01 # define S_IFREG 0x02 # define S_IFLNK 0x04 # define S_IFBLK 0x08 # define S_IFCHR 0x10 # define S_IFSOCK 0x20 #else # include # include # include # include #endif #define S_IFHDN 0x40 #if defined(QNX) || defined(ANDROID) || defined(FREERTOS) # define _fopen_call_ fopen # define _fseek_call_ fseek # define _ftell_call_ ftell # define _stat_struct_ struct stat # define _stat_call_ stat # define _stat_link_ lstat #else # if defined(MAC_OS) # define _fopen_call_ fopen # define _fseek_call_ fseek # define _ftell_call_ ftell # else # ifdef CC_GCC # define _fopen_call_ fopen64 # define _fseek_call_ fseeko64 # define _ftell_call_ ftello64 # else # define _fopen_call_ fopen # define _fseek_call_ fseek # define _ftell_call_ ftell # endif # endif # define _stat_struct_ struct stat64 # define _stat_call_ stat64 # define _stat_link_ lstat64 #endif /*! \class PIFile * \brief Local file * * \section PIFile_sec0 Synopsis * This class provide access to local file. You can manipulate * binary content or use this class as text stream. To binary * access there are function \a read(), \a write(), and many * \a writeBinary() functions. For write variables to file in * their text representation threr are many "<<" operators. * * \section PIFile_sec1 Position * Each opened file has a read/write position - logical position * in the file content you read from or you write to. You can * find out current position with function \a pos(). Function * \a seek(llong position) move position to position "position", * \a seekToBegin() move position to the begin of file, * \a seekToEnd() move position to the end of file. * */ REGISTER_DEVICE(PIFile) PRIVATE_DEFINITION_START(PIFile) FILE * fd; PRIVATE_DEFINITION_END(PIFile) PIString PIFile::FileInfo::name() const { if (path.isEmpty()) return PIString(); return path.mid(path.findLast(PIDir::separator) + 1); } PIString PIFile::FileInfo::baseName() const { if (path.isEmpty()) return PIString(); PIString n = name(), e = extension(); if (e.isEmpty()) return n; return n.cutRight(e.size_s() + 1); } PIString PIFile::FileInfo::extension() const { PIString n = name(); if (n.isEmpty()) return PIString(); while (n.startsWith(".")) n.pop_front(); if (n.isEmpty()) return PIString(); int i = n.findLast("."); if (i < 0) return PIString(); return n.mid(i + 1); } PIString PIFile::FileInfo::dir() const { if (path.isEmpty()) return PIString(); PIString ret = path.mid(0, path.findLast(PIDir::separator)); if (ret.isEmpty()) ret = PIDir::separator; if (!PIDir(ret).isExists()) return (PIStringAscii(".") + PIDir::separator); return ret; } PIFile::PIFile(): PIIODevice() { PRIVATE->fd = 0; fdi = -1; setPrecision(5); } PIFile::PIFile(const PIString & path, PIIODevice::DeviceMode mode): PIIODevice(path, mode) { PRIVATE->fd = 0; fdi = -1; setPrecision(5); if (!path.isEmpty()) open(); } bool PIFile::openTemporary(PIIODevice::DeviceMode mode) { return open(PIString(tmpnam(0)), mode); } //PIFile::PIFile(const PIFile & other) { // PRIVATE->fd = 0; // fdi = -1; // setPrecision(other.prec_); // setPath(other.path()); // mode_ = other.mode_; //} bool PIFile::openDevice() { close(); PIString p = path(); if (p.isEmpty()) return false; if ((mode_ & PIIODevice::WriteOnly) == PIIODevice::WriteOnly) { if (!isExists(p)) { FILE * fd = fopen(p.data(), "w"); if (fd != 0) fclose(fd); } } PRIVATE->fd = _fopen_call_(p.data(), strType(mode_).data()); //piCout << "fopen " << path() << ": " << strType(mode_).data() << PRIVATE->fd; bool opened = (PRIVATE->fd != 0); if (opened) { fdi = fileno(PRIVATE->fd); #ifndef WINDOWS fcntl(fdi, F_SETFL, O_NONBLOCK); #endif _fseek_call_(PRIVATE->fd, 0, SEEK_SET); clearerr(PRIVATE->fd); } //piCout << "open file" << PRIVATE->fd << opened_; return opened; } bool PIFile::closeDevice() { //piCout << "close file" << PRIVATE->fd << opened_; if (isClosed() || PRIVATE->fd == 0) return true; bool cs = (fclose(PRIVATE->fd) == 0); if (cs) PRIVATE->fd = 0; fdi = -1; //piCout << "closed file" << PRIVATE->fd << opened_; return cs; } PIString PIFile::readLine() { PIByteArray str; if (isClosed()) return str; int cc; while (!isEnd()) { cc = fgetc(PRIVATE->fd); if (char(cc) == '\n' || cc == EOF) break; str.push_back(char(cc)); } str.push_back('\0'); if (defaultCharset()) { return PIString::fromCodepage((const char *)str.data(), defaultCharset()); } //cout << "readline: " << str << endl; return PIString::fromUTF8(str); } llong PIFile::readAll(void * data) { llong cp = pos(), s = size(), i = -1; seekToBegin(); if (s < 0) { while (!isEnd()) read(&(((char*)data)[++i]), 1); } else read((char * )data, s); seek(cp); return s; } PIByteArray PIFile::readAll(bool forceRead) { PIByteArray a; llong cp = pos(); if (forceRead) { seekToBegin(); while (!isEnd()) a.push_back(readChar()); seek(cp); return a; } llong s = size(); if (s < 0) return a; a.resize(s); s = readAll(a.data()); seek(cp); if (s >= 0) a.resize(s); return a; } llong PIFile::size() const { if (isClosed()) return -1; llong s, cp = pos(); _fseek_call_(PRIVATE->fd, 0, SEEK_END); clearerr(PRIVATE->fd); s = pos(); _fseek_call_(PRIVATE->fd, cp, SEEK_SET); clearerr(PRIVATE->fd); return s; } void PIFile::resize(llong new_size, uchar fill_) { llong ds = new_size - size(); if (ds == 0) return; if (ds > 0) { uchar * buff = new uchar[ds]; memset(buff, fill_, ds); write(buff, ds); delete[] buff; return; } if (new_size == 0) { clear(); return; } piCoutObj << "Downsize is not supported yet :-("; } bool PIFile::isExists(const PIString & path) { FILE * f = _fopen_call_(PIString(path).data(), "r"); bool ok = (f != 0); if (ok) fclose(f); return ok; } bool PIFile::remove(const PIString & path) { #ifdef WINDOWS if (PIDir::isExists(path)) return RemoveDirectory(path.data()) > 0; else #endif return ::remove(path.data()) == 0; } bool PIFile::rename(const PIString & from, const PIString & to) { return ::rename(from.data(), to.data()) == 0; } PIString PIFile::constructFullPathDevice() const { return path(); } void PIFile::configureFromFullPathDevice(const PIString & full_path) { setPath(full_path); } PIPropertyStorage PIFile::constructVariantDevice() const { PIPropertyStorage ret; PIVariantTypes::File f; f.file = path(); ret.addProperty("path", f); return ret; } void PIFile::configureFromVariantDevice(const PIPropertyStorage & d) { setPath(d.propertyValueByName("path").toString()); } PIString PIFile::strType(const PIIODevice::DeviceMode type) { switch (type) { case PIIODevice::ReadOnly: return "rb"; case PIIODevice::ReadWrite: case PIIODevice::WriteOnly: return "r+b"; } return "rb"; } void PIFile::flush() { if (isOpened()) fflush(PRIVATE->fd); } void PIFile::seek(llong position) { if (isClosed()) return; if (position == pos()) return; _fseek_call_(PRIVATE->fd, position, SEEK_SET); clearerr(PRIVATE->fd); } void PIFile::seekToBegin() { if (isClosed()) return; _fseek_call_(PRIVATE->fd, 0, SEEK_SET); clearerr(PRIVATE->fd); } void PIFile::seekToEnd() { if (isClosed()) return; _fseek_call_(PRIVATE->fd, 0, SEEK_END); clearerr(PRIVATE->fd); } void PIFile::seekToLine(llong line) { if (isClosed()) return; seekToBegin(); piForTimes(line) readLine(); clearerr(PRIVATE->fd); } char PIFile::readChar() { return (char)fgetc(PRIVATE->fd); } void PIFile::setPath(const PIString & path) { PIIODevice::setPath(path); if (isOpened()) open(); } llong PIFile::pos() const { if (isClosed()) return -1; return _ftell_call_(PRIVATE->fd); } bool PIFile::isEnd() const { if (isClosed()) return true; return (feof(PRIVATE->fd) || ferror(PRIVATE->fd)); } void PIFile::setPrecision(int prec) { prec_ = prec; if (prec_ >= 0) prec_str = "." + PIString::fromNumber(prec_); else prec_str = ""; } PIFile &PIFile::operator <<(double v) { if (canWrite() && PRIVATE->fd != 0) ret = fprintf(PRIVATE->fd, ("%" + prec_str + "lf").data(), v); return *this; } PIFile &PIFile::operator >>(double & v) { if (canRead() && PRIVATE->fd != 0) ret = fscanf(PRIVATE->fd, "%lf", &v); return *this; } PIFile &PIFile::operator >>(float & v) { if (canRead() && PRIVATE->fd != 0) ret = fscanf(PRIVATE->fd, "%f", &v); return *this; } PIFile &PIFile::operator >>(ullong & v) { if (canRead() && PRIVATE->fd != 0) ret = fscanf(PRIVATE->fd, "%lln", &v); return *this; } PIFile &PIFile::operator >>(ulong & v) { if (canRead() && PRIVATE->fd != 0) ret = fscanf(PRIVATE->fd, "%ln", &v); return *this; } PIFile &PIFile::operator >>(uint & v) { if (canRead() && PRIVATE->fd != 0) ret = fscanf(PRIVATE->fd, "%n", &v); return *this; } PIFile &PIFile::operator >>(ushort & v) { if (canRead() && PRIVATE->fd != 0) ret = fscanf(PRIVATE->fd, "%hn", &v); return *this; } PIFile &PIFile::operator >>(uchar & v) { if (canRead() && PRIVATE->fd != 0) ret = fscanf(PRIVATE->fd, "%hhn", &v); return *this; } PIFile &PIFile::operator >>(llong & v) { if (canRead() && PRIVATE->fd != 0) ret = fscanf(PRIVATE->fd, "%lln", &v); return *this; } PIFile &PIFile::operator >>(long & v) { if (canRead() && PRIVATE->fd != 0) ret = fscanf(PRIVATE->fd, "%ln", &v); return *this; } PIFile &PIFile::operator >>(int & v) { if (canRead() && PRIVATE->fd != 0) ret = fscanf(PRIVATE->fd, "%n", &v); return *this; } PIFile &PIFile::operator >>(short & v) { if (canRead() && PRIVATE->fd != 0) ret = fscanf(PRIVATE->fd, "%hn", &v); return *this; } PIFile &PIFile::operator >>(char & v) { if (canRead() && PRIVATE->fd != 0) ret = fscanf(PRIVATE->fd, "%hhn", &v); return *this; } PIFile &PIFile::operator <<(float v) { if (canWrite() && PRIVATE->fd != 0) ret = fprintf(PRIVATE->fd, ("%" + prec_str + "f").data(), v); return *this; } PIFile &PIFile::operator <<(ullong v) { if (canWrite() && PRIVATE->fd != 0) ret = fprintf(PRIVATE->fd, "%llu", v); return *this; } PIFile &PIFile::operator <<(ulong v) { if (canWrite() && PRIVATE->fd != 0) ret = fprintf(PRIVATE->fd, "%lu", v); return *this; } PIFile &PIFile::operator <<(uint v) { if (canWrite() && PRIVATE->fd != 0) ret = fprintf(PRIVATE->fd, "%u", v); return *this; } PIFile &PIFile::operator <<(ushort v) { if (canWrite() && PRIVATE->fd != 0) ret = fprintf(PRIVATE->fd, "%hu", v); return *this; } PIFile &PIFile::operator <<(uchar v) { if (canWrite() && PRIVATE->fd != 0) ret = fprintf(PRIVATE->fd, "%u", int(v)); return *this; } PIFile &PIFile::operator <<(llong v) { if (canWrite() && PRIVATE->fd != 0) ret = fprintf(PRIVATE->fd, "%lld", v); return *this; } PIFile &PIFile::operator <<(long v) { if (canWrite() && PRIVATE->fd != 0) ret = fprintf(PRIVATE->fd, "%ld", v); return *this; } PIFile &PIFile::operator <<(int v) { if (canWrite() && PRIVATE->fd != 0) ret = fprintf(PRIVATE->fd, "%d", v); return *this; } PIFile &PIFile::operator <<(short v) { if (canWrite() && PRIVATE->fd != 0) ret = fprintf(PRIVATE->fd, "%hd", v); return *this; } PIFile &PIFile::operator <<(const PIByteArray & v) { if (canWrite() && PRIVATE->fd != 0) write(v.data(), v.size()); return *this; } PIFile &PIFile::operator <<(const char v) { if (canWrite() && PRIVATE->fd != 0) write(&v, 1); return *this; } int PIFile::readDevice(void * read_to, int max_size) { if (!canRead() || PRIVATE->fd == 0) return -1; return fread(read_to, 1, max_size, PRIVATE->fd); } int PIFile::writeDevice(const void * data, int max_size) { if (!canWrite() || PRIVATE->fd == 0) return -1; return fwrite(data, 1, max_size, PRIVATE->fd); } PIFile &PIFile::operator <<(const PIString & v) { if (canWrite() && PRIVATE->fd != 0) *this << v.toCharset(defaultCharset()); return *this; } void PIFile::clear() { close(); PRIVATE->fd = fopen(path().data(), "w"); if (PRIVATE->fd != 0) fclose(PRIVATE->fd); PRIVATE->fd = 0; opened_ = false; open(); } void PIFile::remove() { close(); ::remove(path().data()); } const char * PIFile::defaultCharset() { return PIInit::instance()->file_charset; } void PIFile::setDefaultCharset(const char * c) { PIInit::instance()->setFileCharset(c); } PIFile::FileInfo PIFile::fileInfo(const PIString & path) { FileInfo ret; if (path.isEmpty()) return ret; ret.path = path.replaceAll("\\", PIDir::separator); PIString n = ret.name(); //piCout << "open" << path; #ifdef WINDOWS DWORD attr = GetFileAttributes((LPCTSTR)(path.data())); if (attr == 0xFFFFFFFF) return ret; HANDLE hFile = 0; if ((attr & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) { hFile = CreateFile(path.data(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); } else { hFile = CreateFile(path.data(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); } if (!hFile) return ret; BY_HANDLE_FILE_INFORMATION fi; memset(&fi, 0, sizeof(fi)); if (GetFileInformationByHandle(hFile, &fi) != 0) { LARGE_INTEGER filesize; filesize.LowPart = filesize.HighPart = 0; if (fi.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) ret.flags |= FileInfo::Hidden; if (fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ret.flags |= FileInfo::Dir; else { ret.flags |= FileInfo::File; filesize.LowPart = fi.nFileSizeLow; filesize.HighPart = fi.nFileSizeHigh; } PIString ext = ret.extension(); ret.perm_user = FileInfo::Permissions(true, (attr & FILE_ATTRIBUTE_READONLY) != FILE_ATTRIBUTE_READONLY, ext == "bat" || ext == "exe"); ret.perm_group = ret.perm_other = ret.perm_user; ret.size = filesize.QuadPart; ret.time_access = FILETIME2PIDateTime(fi.ftLastAccessTime); ret.time_modification = FILETIME2PIDateTime(fi.ftLastWriteTime); /*PIByteArray sec; DWORD sec_n(0); //SECURITY_DESCRIPTOR sec; GetFileSecurity(path.data(), DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION, (SECURITY_DESCRIPTOR*)sec.data(), 0, &sec_n); sec.resize(sec_n); GetFileSecurity(path.data(), DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION, (SECURITY_DESCRIPTOR*)sec.data(), sec.size(), &sec_n); errorClear(); SID sid; BOOL def; GetSecurityDescriptorGroup((PSECURITY_DESCRIPTOR)sec.data(), &sid, &def); char * s(0); ConvertSidToStringSid((PSID)&sid, s); piCout << s; LocalFree(s); //ret.id_user = ;*/ } CloseHandle(hFile); #else _stat_struct_ fs; memset(&fs, 0, sizeof(fs)); _stat_call_(path.data(), &fs); int mode = fs.st_mode; ret.size = fs.st_size; ret.id_user = fs.st_uid; ret.id_group = fs.st_gid; #ifdef ANDROID ret.time_access = PIDateTime::fromSystemTime(PISystemTime(fs.st_atime, fs.st_atime_nsec)); ret.time_modification = PIDateTime::fromSystemTime(PISystemTime(fs.st_mtime, fs.st_mtime_nsec)); #else # if defined(QNX) || defined(FREERTOS) ret.time_access = PIDateTime::fromSecondSinceEpoch(fs.st_atime); ret.time_modification = PIDateTime::fromSecondSinceEpoch(fs.st_mtime); # else # ifdef MAC_OS # define ATIME st_atimespec # define MTIME st_ctimespec # else # define ATIME st_atim # define MTIME st_mtim # endif ret.time_access = PIDateTime::fromSystemTime(PISystemTime(fs.ATIME.tv_sec, fs.ATIME.tv_nsec)); ret.time_modification = PIDateTime::fromSystemTime(PISystemTime(fs.MTIME.tv_sec, fs.MTIME.tv_nsec)); # endif #endif #ifndef FREERTOS ret.perm_user = FileInfo::Permissions((mode & S_IRUSR) == S_IRUSR, (mode & S_IWUSR) == S_IWUSR, (mode & S_IXUSR) == S_IXUSR); ret.perm_group = FileInfo::Permissions((mode & S_IRGRP) == S_IRGRP, (mode & S_IWGRP) == S_IWGRP, (mode & S_IXGRP) == S_IXGRP); ret.perm_other = FileInfo::Permissions((mode & S_IROTH) == S_IROTH, (mode & S_IWOTH) == S_IWOTH, (mode & S_IXOTH) == S_IXOTH); memset(&fs, 0, sizeof(fs)); _stat_link_(path.data(), &fs); mode &= ~S_IFLNK; mode |= S_IFLNK & fs.st_mode; if (n.startsWith(".")) mode |= S_IFHDN; if ((mode & S_IFDIR) == S_IFDIR) ret.flags |= FileInfo::Dir; if ((mode & S_IFREG) == S_IFREG) ret.flags |= FileInfo::File; if ((mode & S_IFLNK) == S_IFLNK) ret.flags |= FileInfo::SymbolicLink; if ((mode & S_IFHDN) == S_IFHDN) ret.flags |= FileInfo::Hidden; #endif #endif if (n == ".") ret.flags = FileInfo::Dir | FileInfo::Dot; if (n == "..") ret.flags = FileInfo::Dir | FileInfo::DotDot; return ret; } bool PIFile::applyFileInfo(const PIString & path, const PIFile::FileInfo & info) { if (path.isEmpty()) return false; PIString fp(path); if (fp.endsWith(PIDir::separator)) fp.pop_back(); #ifdef WINDOWS DWORD attr = GetFileAttributes((LPCTSTR)(path.data())); if (attr == 0xFFFFFFFF) return false; attr &= ~(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY); if (info.isHidden()) attr |= FILE_ATTRIBUTE_HIDDEN; if (!info.perm_user.write) attr |= FILE_ATTRIBUTE_READONLY; if (SetFileAttributes((LPCTSTR)(fp.data()), attr) == 0) { piCout << "[PIFile] applyFileInfo: \"SetFileAttributes\" error:" << errorString(); //return false; } HANDLE hFile = 0; if ((attr & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) { hFile = CreateFile(path.data(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); } else { hFile = CreateFile(path.data(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0); } if (!hFile) return false; FILETIME atime = PIDateTime2FILETIME(info.time_access), mtime = PIDateTime2FILETIME(info.time_modification); if (SetFileTime(hFile, 0, &atime, &mtime) == 0) { piCout << "[PIFile] applyFileInfo: \"SetFileTime\" error:" << errorString(); return false; } CloseHandle(hFile); #else //#ifndef ESP_PLATFORM int mode(0); if (info.perm_user.read) mode |= S_IRUSR; if (info.perm_user.write) mode |= S_IWUSR; if (info.perm_user.exec) mode |= S_IXUSR; if (info.perm_group.read) mode |= S_IRGRP; if (info.perm_group.write) mode |= S_IWGRP; if (info.perm_group.exec) mode |= S_IXGRP; if (info.perm_other.read) mode |= S_IROTH; if (info.perm_other.write) mode |= S_IWOTH; if (info.perm_other.exec) mode |= S_IXOTH; if (chmod(fp.data(), mode) != 0) { piCout << "[PIFile] applyFileInfo: \"chmod\" error:" << errorString(); //return false; } if (chown(fp.data(), info.id_user, info.id_group) != 0) { piCout << "[PIFile] applyFileInfo: \"chown\" error:" << errorString(); //return false; } struct timeval tm[2]; PISystemTime st = info.time_access.toSystemTime(); tm[0].tv_sec = st.seconds; tm[0].tv_usec = st.nanoseconds / 1000; st = info.time_modification.toSystemTime(); tm[1].tv_sec = st.seconds; tm[1].tv_usec = st.nanoseconds / 1000; if (utimes(fp.data(), tm) != 0) { piCout << "[PIFile] applyFileInfo: \"utimes\" error:" << errorString(); //return false; } //#endif #endif return true; }