/*---------------------------------------------------------------------------*
* PFileImpl.c *
* *
* Copyright 2007, 2008 Nuance Communciations, Inc. *
* *
* Licensed under the Apache License, Version 2.0 (the 'License'); *
* you may not use this file except in compliance with the License. *
* *
* You may obtain a copy of the License at *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an 'AS IS' BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* *
*---------------------------------------------------------------------------*/
#include "passert.h"
#include "pendian.h"
#include "PFileImpl.h"
#include "PFileSystem.h"
#include "plog.h"
#include "pmemory.h"
#include "pstdio.h"
#include "ptypes.h"
#define MTAG NULL
/**
* Initializes variables declared in the superinterface.
*/
ESR_ReturnCode PFileCreateImpl(PFile* self, const LCHAR* filename, ESR_BOOL isLittleEndian)
{
PFileImpl* impl = (PFileImpl*) self;
ESR_ReturnCode rc;
#ifdef USE_THREAD
ESR_BOOL threadingEnabled;
#endif
#ifdef USE_THREAD
impl->lock = NULL;
#endif
impl->littleEndian = isLittleEndian;
impl->Interface.destroy = &PFileDestroyImpl;
impl->Interface.getFilename = &PFileGetFilenameImpl;
impl->Interface.vfprintf = &PFileVfprintfImpl;
impl->filename = MALLOC(sizeof(LCHAR) * (LSTRLEN(filename) + 1), MTAG);
if (impl->filename == NULL)
{
rc = ESR_OUT_OF_MEMORY;
PLogError(ESR_rc2str(rc));
goto CLEANUP;
}
LSTRCPY(impl->filename, filename);
#ifdef USE_THREAD
rc = PtrdIsEnabled(&threadingEnabled);
if (rc != ESR_SUCCESS)
{
pfprintf(PSTDERR, L("[%s:%d] PtrdIsEnabled failed with %s\n"), __FILE__, __LINE__, ESR_rc2str(rc));
goto CLEANUP;
}
if (threadingEnabled)
{
rc = PtrdMonitorCreate(&impl->lock);
if (rc != ESR_SUCCESS)
goto CLEANUP;
}
#endif
return ESR_SUCCESS;
CLEANUP:
self->destroy(self);
return rc;
}
#ifdef USE_THREAD
#define LOCK_MUTEX(rc, impl) \
if (impl->lock != NULL) \
CHKLOG(rc, PtrdMonitorLock(impl->lock));
#else
#define LOCK_MUTEX(rc, impl)
#endif
#ifdef USE_THREAD
#define CLEANUP_AND_RETURN(rc, impl) \
if (impl->lock!=NULL) \
CHKLOG(rc, PtrdMonitorUnlock(impl->lock)); \
return ESR_SUCCESS; \
CLEANUP: \
if (impl->lock!=NULL) \
PtrdMonitorUnlock(impl->lock); \
return rc;
#else
#define CLEANUP_AND_RETURN(rc, impl) \
return ESR_SUCCESS; \
CLEANUP: \
return rc;
#endif
ESR_ReturnCode PFileDestroyImpl(PFile* self)
{
PFileImpl* impl = (PFileImpl*) self;
ESR_ReturnCode rc;
ESR_BOOL isOpen;
LOCK_MUTEX(rc, impl);
CHKLOG(rc, self->isOpen(self, &isOpen));
if (isOpen)
CHKLOG(rc, self->close(self));
if (impl->filename)
{
FREE(impl->filename);
impl->filename = NULL;
}
#ifdef USE_THREAD
if (impl->lock != NULL)
{
PtrdMonitorUnlock(impl->lock);
rc = PtrdMonitorDestroy(impl->lock);
if (rc != ESR_SUCCESS)
goto CLEANUP;
}
#endif
return ESR_SUCCESS;
CLEANUP:
#ifdef USE_THREAD
if (impl->lock != NULL)
PtrdMonitorUnlock(impl->lock);
#endif
return rc;
}
ESR_ReturnCode PFileGetFilenameImpl(PFile* self, LCHAR* filename, size_t* len)
{
PFileImpl* impl = (PFileImpl*) self;
ESR_ReturnCode rc;
if (self == NULL || len == NULL)
{
PLogError(L("ESR_INVALID_ARGUMENT"));
return ESR_INVALID_ARGUMENT;
}
LOCK_MUTEX(rc, impl);
if (LSTRLEN(impl->filename) + 1 > *len)
{
*len = LSTRLEN(impl->filename) + 1;
rc = ESR_BUFFER_OVERFLOW;
goto CLEANUP;
}
LSTRCPY(filename, impl->filename);
CLEANUP_AND_RETURN(rc, impl);
}
ESR_ReturnCode PFileVfprintfImpl(PFile* self, int* result, const LCHAR* format, va_list args)
{
ESR_ReturnCode rc;
ESR_BOOL isOpen;
#define BUFFER_SIZE 5120
static LCHAR buffer[BUFFER_SIZE];
size_t len;
if (self == NULL)
{
PLogError(L("ESR_INVALID_ARGUMENT"));
return ESR_INVALID_ARGUMENT;
}
CHKLOG(rc, self->isOpen(self, &isOpen));
if (!isOpen)
{
rc = ESR_OPEN_ERROR;
PLogError(L("%s: cannot operate on closed file"), ESR_rc2str(rc));
goto CLEANUP;
}
/*
* fprintf() is computationally expensive, so we compute its output without grabbing a lock
* and only lock while actually writing the results into the file.
*/
if (result != NULL)
*result = vsprintf(buffer, format, args);
else
vsprintf(buffer, format, args);
len = LSTRLEN(buffer);
passert(len < BUFFER_SIZE);
CHKLOG(rc, self->write(self, buffer, sizeof(LCHAR), &len));
return ESR_SUCCESS;
CLEANUP:
return rc;
}