360 lines
8.6 KiB
C++
360 lines
8.6 KiB
C++
/*
|
|
* mpo_fileio.cpp
|
|
*
|
|
* Copyright (C) 2005 Matthew P. Ownby
|
|
*
|
|
* This file is part of MPOLIB, a multi-purpose library
|
|
*
|
|
* MPOLIB 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.
|
|
*
|
|
* MPOLIB 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
|
|
*/
|
|
|
|
// MPO's NOTE:
|
|
// I may wish to use MPOLIB in a proprietary product some day. Therefore,
|
|
// the only way I can accept other people's changes to my code is if they
|
|
// give me full ownership of those changes.
|
|
|
|
// mpo_fileio.cpp
|
|
// by Matt Ownby
|
|
|
|
// Replacement functions for the traditional fopen/fread/fclose/etc functions
|
|
// Main purpose: to support files larger than 4 gigs
|
|
// Secondary purpose: to not rely on MSCVRT dll in windows
|
|
|
|
#include "mpo_fileio.h"
|
|
|
|
#ifndef WIN32
|
|
#include <stdlib.h> // for malloc
|
|
#endif
|
|
|
|
// if this doesn't crash, you're good to go!
|
|
// if anyone knows how to do this check at compile time, let me know!!!
|
|
void mpo_test()
|
|
{
|
|
if (sizeof(MPO_UINT64) != 8) // make sure this is really 64-bit
|
|
{
|
|
int i = 0;
|
|
int b;
|
|
|
|
b = 5 / i; // force crash
|
|
}
|
|
}
|
|
|
|
bool mpo_file_exists(const char *filename)
|
|
{
|
|
bool result = false;
|
|
mpo_io *io = NULL;
|
|
|
|
io = mpo_open(filename, MPO_OPEN_READONLY);
|
|
if (io)
|
|
{
|
|
mpo_close(io);
|
|
result = true;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
mpo_io *mpo_open(const char *filename, int flags)
|
|
{
|
|
mpo_io *io = NULL;
|
|
bool success = false;
|
|
|
|
// dynamically allocate, will be freed by mpo_close
|
|
io = (mpo_io *) malloc(sizeof(mpo_io));
|
|
|
|
#ifdef WIN32
|
|
ZeroMemory(io, sizeof(mpo_io));
|
|
io->handle = INVALID_HANDLE_VALUE;
|
|
|
|
if (flags == MPO_OPEN_READONLY)
|
|
{
|
|
io->handle = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL,
|
|
OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
|
|
}
|
|
else if (flags == MPO_OPEN_READWRITE)
|
|
{
|
|
io->handle = CreateFile(filename, GENERIC_READ | GENERIC_WRITE,0, NULL,
|
|
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
}
|
|
else if (flags == MPO_OPEN_CREATE)
|
|
{
|
|
io->handle = CreateFile(filename, GENERIC_WRITE, 0, NULL,
|
|
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
}
|
|
else if (flags == MPO_OPEN_APPEND) // append
|
|
{
|
|
// will this seek to the end of the file automatically?
|
|
io->handle = CreateFile(filename, GENERIC_WRITE, 0, NULL,
|
|
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (io->handle != INVALID_HANDLE_VALUE)
|
|
{
|
|
mpo_seek(0, MPO_SEEK_END, io); // seek to the end of the file for appending
|
|
}
|
|
}
|
|
else // unknown value, error
|
|
{
|
|
}
|
|
|
|
// if opening the file succeeded
|
|
if (io->handle != INVALID_HANDLE_VALUE)
|
|
{
|
|
/*
|
|
// GetFileSizeEx apparently only works for Win2k and WinXP so I reverted to GetFileSize for the older windows
|
|
LARGE_INTEGER size;
|
|
GetFileSizeEx(io->handle, &size); // get the file size, we always want it..
|
|
io->size = size.QuadPart;
|
|
io->eof = false;
|
|
result = true;
|
|
*/
|
|
|
|
DWORD hSize = 0;
|
|
DWORD lSize = 0;
|
|
lSize = GetFileSize(io->handle, &hSize);
|
|
// if getting the file size succeeded
|
|
if (lSize != INVALID_FILE_SIZE)
|
|
{
|
|
io->size = hSize; // assign higher 32-bits initially
|
|
io->size <<= 32; // shift up 32-bits
|
|
io->size |= lSize; // and merge in lower 32-bit bits
|
|
io->eof = false;
|
|
|
|
// now try to get the file time
|
|
FILETIME last_modified;
|
|
if (GetFileTime(io->handle, NULL, NULL, &last_modified))
|
|
{
|
|
io->time_last_modified = last_modified.dwHighDateTime; // high part
|
|
io->time_last_modified <<= 32; // shift up 32
|
|
io->time_last_modified |= last_modified.dwLowDateTime; // low part
|
|
success = true;
|
|
}
|
|
// else it failed for unknown reasons
|
|
}
|
|
// else getfilesize failed
|
|
}
|
|
#else
|
|
const char *mode = "rb"; // assume reading
|
|
if (flags == MPO_OPEN_CREATE)
|
|
{
|
|
mode = "wb";
|
|
}
|
|
else if (flags == MPO_OPEN_READWRITE)
|
|
{
|
|
if (mpo_file_exists(filename)) mode = "rb+"; // read/write existing file
|
|
else mode = "wb+"; // create file, open in read/write mode
|
|
}
|
|
else if (flags == MPO_OPEN_APPEND)
|
|
{
|
|
mode = "ab";
|
|
}
|
|
// else unknown... eror
|
|
|
|
io->handle = MPO_FOPEN(filename, mode);
|
|
if (io->handle)
|
|
{
|
|
struct stat teh_stats; // to get last modified
|
|
MPO_FSEEK(io->handle, 0, SEEK_END); // go to end of file
|
|
io->size = MPO_FTELL(io->handle); // get position (total file size)
|
|
MPO_FSEEK(io->handle, 0, SEEK_SET); // go to beginning
|
|
io->eof = false;
|
|
if (fstat(fileno(io->handle), &teh_stats) == 0)
|
|
{
|
|
io->time_last_modified = teh_stats.st_mtime;
|
|
success = true;
|
|
}
|
|
// else something went wrong (probably won't ever happen so we won't add error handling)
|
|
}
|
|
#endif
|
|
|
|
// if something went wrong
|
|
if (!success)
|
|
{
|
|
free(io);
|
|
io = NULL;
|
|
}
|
|
|
|
return io;
|
|
}
|
|
|
|
// returns true on success
|
|
// EOF is when bytes_read is 0, but true is returned
|
|
bool mpo_read (void *buf, size_t bytes_to_read,
|
|
MPO_BYTES_READ *bytes_read, mpo_io *io)
|
|
{
|
|
bool result = false;
|
|
MPO_BYTES_READ backup_bytes_read = 0; // in case their 'bytes_read' is NULL
|
|
|
|
// if they don't care what bytes_read is, then we'll just squelch it
|
|
if (bytes_read == NULL)
|
|
{
|
|
bytes_read = &backup_bytes_read;
|
|
}
|
|
|
|
#ifdef WIN32
|
|
LPDWORD ptr = bytes_read;
|
|
if (ReadFile(io->handle, buf, (DWORD) bytes_to_read, ptr, NULL) != 0)
|
|
{
|
|
result = true;
|
|
}
|
|
|
|
/*
|
|
else
|
|
{
|
|
char s[160];
|
|
DWORD i = GetLastError();
|
|
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, i, 0, s, sizeof(s), NULL);
|
|
printline(s);
|
|
tmp = "Error message # is " + g_numstr.ToStr((unsigned int) i);
|
|
printline(tmp.c_str());
|
|
}
|
|
*/
|
|
|
|
#else
|
|
*bytes_read = fread(buf, 1, bytes_to_read, io->handle);
|
|
|
|
// if we read some bytes, or if we properly got to EOF
|
|
if ((*bytes_read > 0) || ((*bytes_read == 0) || feof(io->handle) != 0))
|
|
{
|
|
result = true;
|
|
}
|
|
#endif
|
|
|
|
// if we hit EOF, set a flag for convenience
|
|
if ((*bytes_read) != bytes_to_read)
|
|
{
|
|
io->eof = true;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// returns true on success
|
|
bool mpo_write (const void *buf, size_t bytes_to_write, unsigned int *bytes_written, mpo_io *io)
|
|
{
|
|
bool result = false;
|
|
|
|
unsigned int backup_bytes_written = 0; // in case their 'bytes_written' is NULL
|
|
|
|
// if they don't care what bytes_written is, then we'll just squelch it
|
|
if (bytes_written == NULL)
|
|
{
|
|
bytes_written = &backup_bytes_written;
|
|
}
|
|
|
|
#ifdef WIN32
|
|
LPDWORD ptr = (LPDWORD) bytes_written;
|
|
if (WriteFile(io->handle, buf, (DWORD) bytes_to_write, ptr, NULL) != 0)
|
|
{
|
|
result = true;
|
|
}
|
|
#else
|
|
*bytes_written = fwrite(buf, 1, bytes_to_write, io->handle);
|
|
if (*bytes_written == bytes_to_write)
|
|
{
|
|
result = true;
|
|
}
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
// fseek replacement
|
|
// returns true if seek was successful
|
|
bool mpo_seek(MPO_INT64 offset, seek_type type, mpo_io *io)
|
|
{
|
|
bool result = false;
|
|
|
|
#ifdef WIN32
|
|
/*
|
|
// This code (SetFilePointerEx) works but only for Win2k and WinXP so I have commented it out
|
|
LARGE_INTEGER l;
|
|
l.QuadPart = offset;
|
|
LARGE_INTEGER pre_result;
|
|
pre_result.QuadPart = -1;
|
|
|
|
// if seek is successful
|
|
if (SetFilePointerEx(io->handle, l, &pre_result, type) != 0)
|
|
{
|
|
// result should now contain the current position
|
|
}
|
|
else
|
|
{
|
|
pre_result.QuadPart = -1;
|
|
}
|
|
|
|
result = true; // no decent error checking here
|
|
*/
|
|
|
|
LONG loffset = (LONG) offset; // NOTE : assumes long is 32-bits
|
|
LONG hoffset = (LONG) (offset >> 32);
|
|
DWORD pre_result = 0;
|
|
|
|
pre_result = SetFilePointer(io->handle, loffset, &hoffset, type);
|
|
|
|
// if we potentially got an error ...
|
|
// if (pre_result == INVALID_SET_FILE_POINTER)
|
|
if (pre_result == -1) // INVALID_SET_FILE_POINTER is -1 but some old visual studio 6's don't have this defined
|
|
{
|
|
result = false;
|
|
pre_result = GetLastError(); // check to see if we really got an error
|
|
if (pre_result == NO_ERROR)
|
|
{
|
|
result = true;
|
|
}
|
|
}
|
|
|
|
// no error
|
|
else
|
|
{
|
|
result = true;
|
|
}
|
|
|
|
#else
|
|
int pre_result = MPO_FSEEK(io->handle, offset, type);
|
|
if (pre_result == 0) result = true;
|
|
#endif
|
|
|
|
return result;
|
|
}
|
|
|
|
void mpo_close(mpo_io *io)
|
|
{
|
|
if (io != NULL)
|
|
{
|
|
#ifdef WIN32
|
|
CloseHandle(io->handle);
|
|
#else
|
|
fclose(io->handle);
|
|
#endif
|
|
io->handle = 0;
|
|
io->eof = false;
|
|
io->size = 0;
|
|
io->time_last_modified = 0;
|
|
free(io); // de-allocate
|
|
}
|
|
// else we cannot reference a NULL pointer
|
|
}
|
|
|
|
bool mpo_mkdir(const char *dirname)
|
|
{
|
|
bool result = false;
|
|
#ifdef WIN32
|
|
if (CreateDirectory(dirname, NULL) != 0) result = true;
|
|
#else
|
|
// create directory with minimal permissions
|
|
if (mkdir(dirname, 0700) == 0)
|
|
{
|
|
result = true;
|
|
}
|
|
#endif
|
|
return result;
|
|
}
|