1/*---------------------------------------------------------------------------*
2 *  PFileImpl.c  *
3 *                                                                           *
4 *  Copyright 2007, 2008 Nuance Communciations, Inc.                               *
5 *                                                                           *
6 *  Licensed under the Apache License, Version 2.0 (the 'License');          *
7 *  you may not use this file except in compliance with the License.         *
8 *                                                                           *
9 *  You may obtain a copy of the License at                                  *
10 *      http://www.apache.org/licenses/LICENSE-2.0                           *
11 *                                                                           *
12 *  Unless required by applicable law or agreed to in writing, software      *
13 *  distributed under the License is distributed on an 'AS IS' BASIS,        *
14 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
15 *  See the License for the specific language governing permissions and      *
16 *  limitations under the License.                                           *
17 *                                                                           *
18 *---------------------------------------------------------------------------*/
19
20#include "passert.h"
21#include "pendian.h"
22#include "PFileImpl.h"
23#include "PFileSystem.h"
24#include "plog.h"
25#include "pmemory.h"
26#include "pstdio.h"
27#include "ptypes.h"
28
29#define MTAG NULL
30
31
32/**
33 * Initializes variables declared in the superinterface.
34 */
35ESR_ReturnCode PFileCreateImpl(PFile* self, const LCHAR* filename, ESR_BOOL isLittleEndian)
36{
37  PFileImpl* impl = (PFileImpl*) self;
38  ESR_ReturnCode rc;
39#ifdef USE_THREAD
40  ESR_BOOL threadingEnabled;
41#endif
42
43#ifdef USE_THREAD
44  impl->lock = NULL;
45#endif
46  impl->littleEndian = isLittleEndian;
47
48  impl->Interface.destroy = &PFileDestroyImpl;
49  impl->Interface.getFilename = &PFileGetFilenameImpl;
50  impl->Interface.vfprintf = &PFileVfprintfImpl;
51  impl->filename = MALLOC(sizeof(LCHAR) * (LSTRLEN(filename) + 1), MTAG);
52
53  if (impl->filename == NULL)
54  {
55    rc = ESR_OUT_OF_MEMORY;
56    PLogError(ESR_rc2str(rc));
57    goto CLEANUP;
58  }
59  LSTRCPY(impl->filename, filename);
60
61#ifdef USE_THREAD
62  rc = PtrdIsEnabled(&threadingEnabled);
63  if (rc != ESR_SUCCESS)
64  {
65    pfprintf(PSTDERR, L("[%s:%d] PtrdIsEnabled failed with %s\n"), __FILE__, __LINE__, ESR_rc2str(rc));
66    goto CLEANUP;
67  }
68  if (threadingEnabled)
69  {
70    rc = PtrdMonitorCreate(&impl->lock);
71    if (rc != ESR_SUCCESS)
72      goto CLEANUP;
73  }
74#endif
75  return ESR_SUCCESS;
76CLEANUP:
77  self->destroy(self);
78  return rc;
79}
80
81
82#ifdef USE_THREAD
83#define LOCK_MUTEX(rc, impl) \
84  if (impl->lock != NULL) \
85    CHKLOG(rc, PtrdMonitorLock(impl->lock));
86#else
87#define LOCK_MUTEX(rc, impl)
88#endif
89
90
91#ifdef USE_THREAD
92#define CLEANUP_AND_RETURN(rc, impl) \
93  if (impl->lock!=NULL) \
94    CHKLOG(rc, PtrdMonitorUnlock(impl->lock)); \
95  return ESR_SUCCESS; \
96  CLEANUP: \
97  if (impl->lock!=NULL) \
98    PtrdMonitorUnlock(impl->lock); \
99  return rc;
100#else
101#define CLEANUP_AND_RETURN(rc, impl) \
102  return ESR_SUCCESS; \
103  CLEANUP: \
104  return rc;
105#endif
106
107
108ESR_ReturnCode PFileDestroyImpl(PFile* self)
109{
110  PFileImpl* impl = (PFileImpl*) self;
111  ESR_ReturnCode rc;
112  ESR_BOOL isOpen;
113
114  LOCK_MUTEX(rc, impl);
115  CHKLOG(rc, self->isOpen(self, &isOpen));
116  if (isOpen)
117    CHKLOG(rc, self->close(self));
118  if (impl->filename)
119  {
120    FREE(impl->filename);
121    impl->filename = NULL;
122  }
123#ifdef USE_THREAD
124  if (impl->lock != NULL)
125  {
126    PtrdMonitorUnlock(impl->lock);
127    rc = PtrdMonitorDestroy(impl->lock);
128    if (rc != ESR_SUCCESS)
129      goto CLEANUP;
130  }
131#endif
132  return ESR_SUCCESS;
133CLEANUP:
134#ifdef USE_THREAD
135  if (impl->lock != NULL)
136    PtrdMonitorUnlock(impl->lock);
137#endif
138  return rc;
139}
140
141ESR_ReturnCode PFileGetFilenameImpl(PFile* self, LCHAR* filename, size_t* len)
142{
143  PFileImpl* impl = (PFileImpl*) self;
144  ESR_ReturnCode rc;
145
146  if (self == NULL || len == NULL)
147  {
148    PLogError(L("ESR_INVALID_ARGUMENT"));
149    return ESR_INVALID_ARGUMENT;
150  }
151  LOCK_MUTEX(rc, impl);
152  if (LSTRLEN(impl->filename) + 1 > *len)
153  {
154    *len = LSTRLEN(impl->filename) + 1;
155    rc = ESR_BUFFER_OVERFLOW;
156    goto CLEANUP;
157  }
158  LSTRCPY(filename, impl->filename);
159  CLEANUP_AND_RETURN(rc, impl);
160}
161
162ESR_ReturnCode PFileVfprintfImpl(PFile* self, int* result, const LCHAR* format, va_list args)
163{
164  ESR_ReturnCode rc;
165  ESR_BOOL isOpen;
166#define BUFFER_SIZE 5120
167  static LCHAR buffer[BUFFER_SIZE];
168  size_t len;
169
170  if (self == NULL)
171  {
172    PLogError(L("ESR_INVALID_ARGUMENT"));
173    return ESR_INVALID_ARGUMENT;
174  }
175
176  CHKLOG(rc, self->isOpen(self, &isOpen));
177  if (!isOpen)
178  {
179    rc = ESR_OPEN_ERROR;
180    PLogError(L("%s: cannot operate on closed file"), ESR_rc2str(rc));
181    goto CLEANUP;
182  }
183
184  /*
185   * fprintf() is computationally expensive, so we compute its output without grabbing a lock
186   * and only lock while actually writing the results into the file.
187   */
188  if (result != NULL)
189    *result = vsprintf(buffer, format, args);
190  else
191    vsprintf(buffer, format, args);
192  len = LSTRLEN(buffer);
193  passert(len < BUFFER_SIZE);
194
195  CHKLOG(rc, self->write(self, buffer, sizeof(LCHAR), &len));
196  return ESR_SUCCESS;
197CLEANUP:
198  return rc;
199}
200