733 lines
18 KiB
C++
Executable File
733 lines
18 KiB
C++
Executable File
/*
|
|
PIP - Platform Independent Primitives
|
|
File
|
|
Copyright (C) 2017 Ivan Pelipenko peri4ko@yandex.ru
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU 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 General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#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 <sys/stat.h>
|
|
# include <sys/time.h>
|
|
# include <fcntl.h>
|
|
# include <utime.h>
|
|
#endif
|
|
#define S_IFHDN 0x40
|
|
#if defined(QNX) || defined(ANDROID)
|
|
# 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
|
|
# define _fopen_call_ fopen64
|
|
# define _fseek_call_ fseeko64
|
|
# define _ftell_call_ ftello64
|
|
# 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)
|
|
|
|
|
|
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.find(".");
|
|
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;
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
|
|
PIFile::PIFile(): PIIODevice() {
|
|
fd = 0;
|
|
fdi = -1;
|
|
setPrecision(5);
|
|
}
|
|
|
|
|
|
PIFile::PIFile(const PIString & path, PIIODevice::DeviceMode mode): PIIODevice(path, mode) {
|
|
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) {
|
|
// 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);
|
|
}
|
|
}
|
|
fd = _fopen_call_(p.data(), strType(mode_).data());
|
|
//piCout << "fopen " << path() << ": " << strType(mode_).data() << fd;
|
|
bool opened = (fd != 0);
|
|
if (opened) {
|
|
fdi = fileno(fd);
|
|
#ifndef WINDOWS
|
|
fcntl(fdi, F_SETFL, O_NONBLOCK);
|
|
#endif
|
|
_fseek_call_(fd, 0, SEEK_SET);
|
|
clearerr(fd);
|
|
}
|
|
//piCout << "open file" << fd << opened_;
|
|
return opened;
|
|
}
|
|
|
|
|
|
bool PIFile::closeDevice() {
|
|
//piCout << "close file" << fd << opened_;
|
|
if (isClosed() || fd == 0) return true;
|
|
bool cs = (fclose(fd) == 0);
|
|
if (cs) fd = 0;
|
|
fdi = -1;
|
|
//piCout << "closed file" << fd << opened_;
|
|
return cs;
|
|
}
|
|
|
|
|
|
PIString PIFile::readLine() {
|
|
PIByteArray str;
|
|
if (isClosed()) return str;
|
|
int cc;
|
|
while (!isEnd()) {
|
|
cc = fgetc(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(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_(fd, 0, SEEK_END); clearerr(fd);
|
|
s = pos();
|
|
_fseek_call_(fd, cp, SEEK_SET); clearerr(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);
|
|
}
|
|
|
|
|
|
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(fd);
|
|
}
|
|
|
|
|
|
void PIFile::seek(llong position) {
|
|
if (isClosed()) return;
|
|
if (position == pos()) return;
|
|
_fseek_call_(fd, position, SEEK_SET);
|
|
clearerr(fd);
|
|
}
|
|
|
|
|
|
void PIFile::seekToBegin() {
|
|
if (isClosed()) return;
|
|
_fseek_call_(fd, 0, SEEK_SET);
|
|
clearerr(fd);
|
|
}
|
|
|
|
|
|
void PIFile::seekToEnd() {
|
|
if (isClosed()) return;
|
|
_fseek_call_(fd, 0, SEEK_END);
|
|
clearerr(fd);
|
|
}
|
|
|
|
|
|
void PIFile::seekToLine(llong line) {
|
|
if (isClosed()) return;
|
|
seekToBegin();
|
|
piForTimes(line) readLine();
|
|
clearerr(fd);
|
|
}
|
|
|
|
|
|
char PIFile::readChar() {
|
|
return (char)fgetc(fd);
|
|
}
|
|
|
|
|
|
void PIFile::setPath(const PIString & path) {
|
|
PIIODevice::setPath(path);
|
|
if (isOpened()) open();
|
|
}
|
|
|
|
|
|
llong PIFile::pos() const {
|
|
if (isClosed()) return -1;
|
|
return _ftell_call_(fd);
|
|
}
|
|
|
|
|
|
bool PIFile::isEnd() const {
|
|
if (isClosed()) return true;
|
|
return (feof(fd) || ferror(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() && fd != 0) ret = fprintf(fd, ("%" + prec_str + "lf").data(), v);
|
|
return *this;
|
|
}
|
|
|
|
|
|
PIFile &PIFile::operator >>(double & v) {
|
|
if (canRead() && fd != 0) ret = fscanf(fd, "%lf", &v);
|
|
return *this;
|
|
}
|
|
|
|
|
|
PIFile &PIFile::operator >>(float & v) {
|
|
if (canRead() && fd != 0) ret = fscanf(fd, "%f", &v);
|
|
return *this;
|
|
}
|
|
|
|
|
|
PIFile &PIFile::operator >>(ullong & v) {
|
|
if (canRead() && fd != 0) ret = fscanf(fd, "%lln", &v);
|
|
return *this;
|
|
}
|
|
|
|
|
|
PIFile &PIFile::operator >>(ulong & v) {
|
|
if (canRead() && fd != 0) ret = fscanf(fd, "%ln", &v);
|
|
return *this;
|
|
}
|
|
|
|
|
|
PIFile &PIFile::operator >>(uint & v) {
|
|
if (canRead() && fd != 0) ret = fscanf(fd, "%n", &v);
|
|
return *this;
|
|
}
|
|
|
|
|
|
PIFile &PIFile::operator >>(ushort & v) {
|
|
if (canRead() && fd != 0) ret = fscanf(fd, "%hn", &v);
|
|
return *this;
|
|
}
|
|
|
|
|
|
PIFile &PIFile::operator >>(uchar & v) {
|
|
if (canRead() && fd != 0) ret = fscanf(fd, "%hhn", &v);
|
|
return *this;
|
|
}
|
|
|
|
|
|
PIFile &PIFile::operator >>(llong & v) {
|
|
if (canRead() && fd != 0) ret = fscanf(fd, "%lln", &v);
|
|
return *this;
|
|
}
|
|
|
|
|
|
PIFile &PIFile::operator >>(long & v) {
|
|
if (canRead() && fd != 0) ret = fscanf(fd, "%ln", &v);
|
|
return *this;
|
|
}
|
|
|
|
|
|
PIFile &PIFile::operator >>(int & v) {
|
|
if (canRead() && fd != 0) ret = fscanf(fd, "%n", &v);
|
|
return *this;
|
|
}
|
|
|
|
|
|
PIFile &PIFile::operator >>(short & v) {
|
|
if (canRead() && fd != 0) ret = fscanf(fd, "%hn", &v);
|
|
return *this;
|
|
}
|
|
|
|
|
|
PIFile &PIFile::operator >>(char & v) {
|
|
if (canRead() && fd != 0) ret = fscanf(fd, "%hhn", &v);
|
|
return *this;
|
|
}
|
|
|
|
|
|
PIFile &PIFile::operator <<(float v) {
|
|
if (canWrite() && fd != 0) ret = fprintf(fd, ("%" + prec_str + "f").data(), v);
|
|
return *this;
|
|
}
|
|
|
|
|
|
PIFile &PIFile::operator <<(ullong v) {
|
|
if (canWrite() && fd != 0) ret = fprintf(fd, "%llu", v);
|
|
return *this;
|
|
}
|
|
|
|
|
|
PIFile &PIFile::operator <<(ulong v) {
|
|
if (canWrite() && fd != 0) ret = fprintf(fd, "%lu", v);
|
|
return *this;
|
|
}
|
|
|
|
|
|
PIFile &PIFile::operator <<(uint v) {
|
|
if (canWrite() && fd != 0) ret = fprintf(fd, "%u", v);
|
|
return *this;
|
|
}
|
|
|
|
|
|
PIFile &PIFile::operator <<(ushort v) {
|
|
if (canWrite() && fd != 0) ret = fprintf(fd, "%hu", v);
|
|
return *this;
|
|
}
|
|
|
|
|
|
PIFile &PIFile::operator <<(uchar v) {
|
|
if (canWrite() && fd != 0) ret = fprintf(fd, "%u", int(v));
|
|
return *this;
|
|
}
|
|
|
|
|
|
PIFile &PIFile::operator <<(llong v) {
|
|
if (canWrite() && fd != 0) ret = fprintf(fd, "%lld", v);
|
|
return *this;
|
|
}
|
|
|
|
|
|
PIFile &PIFile::operator <<(long v) {
|
|
if (canWrite() && fd != 0) ret = fprintf(fd, "%ld", v);
|
|
return *this;
|
|
}
|
|
|
|
|
|
PIFile &PIFile::operator <<(int v) {
|
|
if (canWrite() && fd != 0) ret = fprintf(fd, "%d", v);
|
|
return *this;
|
|
}
|
|
|
|
|
|
PIFile &PIFile::operator <<(short v) {
|
|
if (canWrite() && fd != 0) ret = fprintf(fd, "%hd", v);
|
|
return *this;
|
|
}
|
|
|
|
|
|
PIFile &PIFile::operator <<(const PIByteArray & v) {
|
|
if (canWrite() && fd != 0) write(v.data(), v.size());
|
|
return *this;
|
|
}
|
|
|
|
|
|
PIFile &PIFile::operator <<(const char v) {
|
|
if (canWrite() && fd != 0) write(&v, 1);
|
|
return *this;
|
|
}
|
|
|
|
|
|
int PIFile::readDevice(void * read_to, int max_size) {
|
|
if (!canRead() || fd == 0) return -1;
|
|
return fread(read_to, 1, max_size, fd);
|
|
}
|
|
|
|
|
|
int PIFile::writeDevice(const void * data, int max_size) {
|
|
if (!canWrite() || fd == 0) return -1;
|
|
return fwrite(data, 1, max_size, fd);
|
|
}
|
|
|
|
|
|
PIFile &PIFile::operator <<(const PIString & v) {
|
|
if (canWrite() && fd != 0)
|
|
*this << v.toCharset(defaultCharset());
|
|
return *this;
|
|
}
|
|
|
|
|
|
void PIFile::clear() {
|
|
close();
|
|
fd = fopen(path().data(), "w");
|
|
if (fd != 0) fclose(fd);
|
|
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
|
|
# ifdef QNX
|
|
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
|
|
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
|
|
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
|
|
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
|
|
return true;
|
|
}
|