// This file is part of par2cmdline (a PAR 2.0 compatible file verification and
// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0.
//
// Copyright (c) 2003 Peter Brian Clements
//
// par2cmdline 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 2 of the License, or
// (at your option) any later version.
//
// par2cmdline 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, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "par2cmdline.h"
#ifdef _MSC_VER
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
#endif
#ifdef WIN32
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define OffsetType __int64
#define MaxOffset 0x7fffffffffffffffI64
#define LengthType unsigned int
#define MaxLength 0xffffffffUL
DiskFile::DiskFile(void)
{
filesize = 0;
offset = 0;
hFile = INVALID_HANDLE_VALUE;
exists = false;
}
DiskFile::~DiskFile(void)
{
if (hFile != INVALID_HANDLE_VALUE)
::CloseHandle(hFile);
}
// Open the file
bool DiskFile::Open(string _filename, u64 _filesize)
{
assert(hFile == INVALID_HANDLE_VALUE);
filename = _filename;
filesize = _filesize;
hFile = ::CreateFileA(_filename.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
DWORD error = ::GetLastError();
switch (error)
{
case ERROR_FILE_NOT_FOUND:
case ERROR_PATH_NOT_FOUND:
break;
default:
cerr << "Could not open \"" << _filename << "\": " << ErrorMessage(error) << endl;
}
return false;
}
offset = 0;
exists = true;
return true;
}
// Read some data from disk
bool DiskFile::Read(u64 _offset, void *buffer, size_t length)
{
assert(hFile != INVALID_HANDLE_VALUE);
if (offset != _offset)
{
LONG lowoffset = ((LONG*)&_offset)[0];
LONG highoffset = ((LONG*)&_offset)[1];
// Seek to the required offset
if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, lowoffset, &highoffset, FILE_BEGIN))
{
DWORD error = ::GetLastError();
cerr << "Could not read " << (u64)length << " bytes from \"" << filename << "\" at offset " << _offset << ": " << ErrorMessage(error) << endl;
return false;
}
offset = _offset;
}
if (length > MaxLength)
{
cerr << "Could not read " << (u64)length << " bytes from \"" << filename << "\" at offset " << _offset << ": " << "Read too long" << endl;
return false;
}
DWORD want = (LengthType)length;
DWORD got;
// Read the data
if (!::ReadFile(hFile, buffer, want, &got, NULL))
{
DWORD error = ::GetLastError();
cerr << "Could not read " << (u64)length << " bytes from \"" << filename << "\" at offset " << _offset << ": " << ErrorMessage(error) << endl;
return false;
}
offset += length;
return true;
}
void DiskFile::Close(void)
{
if (hFile != INVALID_HANDLE_VALUE)
{
::CloseHandle(hFile);
hFile = INVALID_HANDLE_VALUE;
}
}
string DiskFile::GetCanonicalPathname(string filename)
{
char fullname[MAX_PATH];
char *filepart;
// Resolve a relative path to a full path
int length = ::GetFullPathName(filename.c_str(), sizeof(fullname), fullname, &filepart);
if (length <= 0 || sizeof(fullname) < length)
return filename;
// Make sure the drive letter is upper case.
fullname[0] = toupper(fullname[0]);
// Translate all /'s to \'s
char *current = strchr(fullname, '/');
while (current)
{
*current++ = '\\';
current = strchr(current, '/');
}
// Copy the root directory to the output string
string longname(fullname, 3);
// Start processing at the first path component
current = &fullname[3];
char *limit = &fullname[length];
// Process until we reach the end of the full name
while (current < limit)
{
char *tail;
// Find the next \, or the end of the string
(tail = strchr(current, '\\')) || (tail = limit);
*tail = 0;
// Create a wildcard to search for the path
string wild = longname + current;
WIN32_FIND_DATA finddata;
HANDLE hFind = ::FindFirstFile(wild.c_str(), &finddata);
if (hFind == INVALID_HANDLE_VALUE)
{
// If the component was not found then just copy the rest of the path to the
// output buffer verbatim.
longname += current;
break;
}
::FindClose(hFind);
// Copy the component found to the output
longname += finddata.cFileName;
current = tail + 1;
// If we have not reached the end of the name, add a "\"
if (current < limit)
longname += '\\';
}
return longname;
}
list<string>* DiskFile::FindFiles(string path, string wildcard)
{
list<string> *matches = new list<string>;
wildcard = path + wildcard;
WIN32_FIND_DATA fd;
HANDLE h = ::FindFirstFile(wildcard.c_str(), &fd);
if (h != INVALID_HANDLE_VALUE)
{
do
{
if (0 == (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
matches->push_back(path + fd.cFileName);
}
} while (::FindNextFile(h, &fd));
::FindClose(h);
}
return matches;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#else // !WIN32
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define OffsetType long
#define MaxOffset 0x7fffffffL
#define LengthType unsigned int
#define MaxLength 0xffffffffUL
DiskFile::DiskFile(void)
{
//filename;
filesize = 0;
offset = 0;
file = 0;
exists = false;
}
DiskFile::~DiskFile(void)
{
if (file != 0)
fclose(file);
}
// Open the file
bool DiskFile::Open(string _filename, u64 _filesize)
{
assert(file == 0);
filename = _filename;
filesize = _filesize;
if (_filesize > MaxOffset)
{
cerr << "File size for " << _filename << " is too large." << endl;
return false;
}
file = fopen(filename.c_str(), "rb");
if (file == 0)
{
return false;
}
offset = 0;
exists = true;
return true;
}
// Read some data from disk
bool DiskFile::Read(u64 _offset, void *buffer, size_t length)
{
assert(file != 0);
if (offset != _offset)
{
if (_offset > MaxOffset)
{
cerr << "Could not read " << (u64)length << " bytes from " << filename << " at offset " << _offset << endl;
return false;
}
if (fseek(file, (OffsetType)_offset, SEEK_SET))
{
cerr << "Could not read " << (u64)length << " bytes from " << filename << " at offset " << _offset << endl;
return false;
}
offset = _offset;
}
if (length > MaxLength)
{
cerr << "Could not read " << (u64)length << " bytes from " << filename << " at offset " << _offset << endl;
return false;
}
if (1 != fread(buffer, (LengthType)length, 1, file))
{
cerr << "Could not read " << (u64)length << " bytes from " << filename << " at offset " << _offset << endl;
return false;
}
offset += length;
return true;
}
void DiskFile::Close(void)
{
if (file != 0)
{
fclose(file);
file = 0;
}
}
// Attempt to get the full pathname of the file
string DiskFile::GetCanonicalPathname(string filename)
{
// Is the supplied path already an absolute one
if (filename.size() == 0 || filename[0] == '/')
return filename;
// Get the current directory
char curdir[1000];
if (0 == getcwd(curdir, sizeof(curdir)))
{
return filename;
}
// Allocate a work buffer and copy the resulting full path into it.
char *work = new char[strlen(curdir) + filename.size() + 2];
strcpy(work, curdir);
if (work[strlen(work)-1] != '/')
strcat(work, "/");
strcat(work, filename.c_str());
char *in = work;
char *out = work;
while (*in)
{
if (*in == '/')
{
if (in[1] == '.' && in[2] == '/')
{
// skip the input past /./
in += 2;
}
else if (in[1] == '.' && in[2] == '.' && in[3] == '/')
{
// backtrack the output if /../ was found on the input
in += 3;
if (out > work)
{
do
{
out--;
} while (out > work && *out != '/');
}
}
else
{
*out++ = *in++;
}
}
else
{
*out++ = *in++;
}
}
*out = 0;
string result = work;
delete [] work;
return result;
}
list<string>* DiskFile::FindFiles(string path, string wildcard)
{
list<string> *matches = new list<string>;
string::size_type where;
if ((where = wildcard.find_first_of('*')) != string::npos ||
(where = wildcard.find_first_of('?')) != string::npos)
{
string front = wildcard.substr(0, where);
bool multiple = wildcard[where] == '*';
string back = wildcard.substr(where+1);
DIR *dirp = opendir(path.c_str());
if (dirp != 0)
{
struct dirent *d;
while ((d = readdir(dirp)) != 0)
{
string name = d->d_name;
if (name == "." || name == "..")
continue;
if (multiple)
{
if (name.size() >= wildcard.size() &&
name.substr(0, where) == front &&
name.substr(name.size()-back.size()) == back)
{
matches->push_back(path + name);
}
}
else
{
if (name.size() == wildcard.size())
{
string::const_iterator pw = wildcard.begin();
string::const_iterator pn = name.begin();
while (pw != wildcard.end())
{
if (*pw != '?' && *pw != *pn)
break;
++pw;
++pn;
}
if (pw == wildcard.end())
{
matches->push_back(path + name);
}
}
}
}
closedir(dirp);
}
}
else
{
struct stat st;
string fn = path + wildcard;
if (stat(fn.c_str(), &st) == 0)
{
matches->push_back(path + wildcard);
}
}
return matches;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#endif
bool DiskFile::Open(void)
{
string _filename = filename;
return Open(_filename);
}
bool DiskFile::Open(string _filename)
{
return Open(_filename, GetFileSize(_filename));
}
//string DiskFile::GetPathFromFilename(string filename)
//{
// string::size_type where;
//
// if (string::npos != (where = filename.find_last_of('/')) ||
// string::npos != (where = filename.find_last_of('\\')))
// {
// return filename.substr(0, where+1);
// }
// else
// {
// return "." PATHSEP;
// }
//}
void DiskFile::SplitFilename(string filename, string &path, string &name)
{
string::size_type where;
if (string::npos != (where = filename.find_last_of('/')) ||
string::npos != (where = filename.find_last_of('\\')))
{
path = filename.substr(0, where+1);
name = filename.substr(where+1);
}
else
{
path = "." SPATHSEP;
name = filename;
}
}
bool DiskFile::FileExists(string filename)
{
struct stat st;
return ((0 == stat(filename.c_str(), &st)) && (0 != (st.st_mode & S_IFREG)));
}
u64 DiskFile::GetFileSize(string filename)
{
struct stat st;
if ((0 == stat(filename.c_str(), &st)) && (0 != (st.st_mode & S_IFREG)))
{
return st.st_size;
}
else
{
return 0;
}
}
// Take a filename from a PAR2 file and replace any characters
// which would be illegal for a file on disk
string DiskFile::TranslateFilename(string filename)
{
string result;
string::iterator p = filename.begin();
while (p != filename.end())
{
unsigned char ch = *p;
bool ok = true;
#ifdef WIN32
if (ch < 32)
{
ok = false;
}
else
{
switch (ch)
{
case '"':
case '*':
case '/':
case ':':
case '<':
case '>':
case '?':
case '\\':
case '|':
ok = false;
}
}
#else
if (ch < 32)
{
ok = false;
}
else
{
switch (ch)
{
case '/':
ok = false;
}
}
#endif
if (ok)
{
result += ch;
}
else
{
// convert problem characters to hex
result += ((ch >> 4) < 10) ? (ch >> 4) + '0' : (ch >> 4) + 'A'-10;
result += ((ch & 0xf) < 10) ? (ch & 0xf) + '0' : (ch & 0xf) + 'A'-10;
}
++p;
}
return result;
}
#ifdef WIN32
string DiskFile::ErrorMessage(DWORD error)
{
string result;
LPVOID lpMsgBuf;
if (::FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR)&lpMsgBuf,
0,
NULL))
{
result = (char*)lpMsgBuf;
LocalFree(lpMsgBuf);
}
else
{
char message[40];
_snprintf(message, sizeof(message), "Unknown error code (%d)", error);
result = message;
}
return result;
}
#endif
DiskFileMap::DiskFileMap(void)
{
}
DiskFileMap::~DiskFileMap(void)
{
map<string, DiskFile*>::iterator fi = diskfilemap.begin();
while (fi != diskfilemap.end())
{
delete (*fi).second;
++fi;
}
}
bool DiskFileMap::Insert(DiskFile *diskfile)
{
string filename = diskfile->FileName();
assert(filename.length() != 0);
pair<map<string,DiskFile*>::const_iterator,bool> location = diskfilemap.insert(pair<string,DiskFile*>(filename, diskfile));
return location.second;
}
void DiskFileMap::Remove(DiskFile *diskfile)
{
string filename = diskfile->FileName();
assert(filename.length() != 0);
diskfilemap.erase(filename);
}
DiskFile* DiskFileMap::Find(string filename) const
{
assert(filename.length() != 0);
map<string, DiskFile*>::const_iterator f = diskfilemap.find(filename);
return (f != diskfilemap.end()) ? f->second : 0;
}
syntax highlighted by Code2HTML, v. 0.9.1