1/*
2 *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "webrtc/system_wrappers/source/file_impl.h"
12
13#include <assert.h>
14
15#ifdef _WIN32
16#include <Windows.h>
17#else
18#include <stdarg.h>
19#include <string.h>
20#endif
21
22#include "webrtc/base/checks.h"
23#include "webrtc/system_wrappers/include/rw_lock_wrapper.h"
24
25namespace webrtc {
26
27FileWrapper* FileWrapper::Create() {
28  return new FileWrapperImpl();
29}
30
31FileWrapperImpl::FileWrapperImpl()
32    : rw_lock_(RWLockWrapper::CreateRWLock()),
33      id_(NULL),
34      managed_file_handle_(true),
35      open_(false),
36      looping_(false),
37      read_only_(false),
38      max_size_in_bytes_(0),
39      size_in_bytes_(0) {
40  memset(file_name_utf8_, 0, kMaxFileNameSize);
41}
42
43FileWrapperImpl::~FileWrapperImpl() {
44  if (id_ != NULL && managed_file_handle_) {
45    fclose(id_);
46  }
47}
48
49int FileWrapperImpl::CloseFile() {
50  WriteLockScoped write(*rw_lock_);
51  return CloseFileImpl();
52}
53
54int FileWrapperImpl::Rewind() {
55  WriteLockScoped write(*rw_lock_);
56  if (looping_ || !read_only_) {
57    if (id_ != NULL) {
58      size_in_bytes_ = 0;
59      return fseek(id_, 0, SEEK_SET);
60    }
61  }
62  return -1;
63}
64
65int FileWrapperImpl::SetMaxFileSize(size_t bytes) {
66  WriteLockScoped write(*rw_lock_);
67  max_size_in_bytes_ = bytes;
68  return 0;
69}
70
71int FileWrapperImpl::Flush() {
72  WriteLockScoped write(*rw_lock_);
73  return FlushImpl();
74}
75
76int FileWrapperImpl::FileName(char* file_name_utf8, size_t size) const {
77  ReadLockScoped read(*rw_lock_);
78  size_t length = strlen(file_name_utf8_);
79  if (length > kMaxFileNameSize) {
80    assert(false);
81    return -1;
82  }
83  if (length < 1) {
84    return -1;
85  }
86
87  // Make sure to NULL terminate
88  if (size < length) {
89    length = size - 1;
90  }
91  memcpy(file_name_utf8, file_name_utf8_, length);
92  file_name_utf8[length] = 0;
93  return 0;
94}
95
96bool FileWrapperImpl::Open() const {
97  ReadLockScoped read(*rw_lock_);
98  return open_;
99}
100
101int FileWrapperImpl::OpenFile(const char* file_name_utf8, bool read_only,
102                              bool loop, bool text) {
103  WriteLockScoped write(*rw_lock_);
104  if (id_ != NULL && !managed_file_handle_)
105    return -1;
106  size_t length = strlen(file_name_utf8);
107  if (length > kMaxFileNameSize - 1) {
108    return -1;
109  }
110
111  read_only_ = read_only;
112
113  FILE* tmp_id = NULL;
114#if defined _WIN32
115  wchar_t wide_file_name[kMaxFileNameSize];
116  wide_file_name[0] = 0;
117
118  MultiByteToWideChar(CP_UTF8,
119                      0,  // UTF8 flag
120                      file_name_utf8,
121                      -1,  // Null terminated string
122                      wide_file_name,
123                      kMaxFileNameSize);
124  if (text) {
125    if (read_only) {
126      tmp_id = _wfopen(wide_file_name, L"rt");
127    } else {
128      tmp_id = _wfopen(wide_file_name, L"wt");
129    }
130  } else {
131    if (read_only) {
132      tmp_id = _wfopen(wide_file_name, L"rb");
133    } else {
134      tmp_id = _wfopen(wide_file_name, L"wb");
135    }
136  }
137#else
138  if (text) {
139    if (read_only) {
140      tmp_id = fopen(file_name_utf8, "rt");
141    } else {
142      tmp_id = fopen(file_name_utf8, "wt");
143    }
144  } else {
145    if (read_only) {
146      tmp_id = fopen(file_name_utf8, "rb");
147    } else {
148      tmp_id = fopen(file_name_utf8, "wb");
149    }
150  }
151#endif
152
153  if (tmp_id != NULL) {
154    // +1 comes from copying the NULL termination character.
155    memcpy(file_name_utf8_, file_name_utf8, length + 1);
156    if (id_ != NULL) {
157      fclose(id_);
158    }
159    id_ = tmp_id;
160    managed_file_handle_ = true;
161    looping_ = loop;
162    open_ = true;
163    return 0;
164  }
165  return -1;
166}
167
168int FileWrapperImpl::OpenFromFileHandle(FILE* handle,
169                                        bool manage_file,
170                                        bool read_only,
171                                        bool loop) {
172  WriteLockScoped write(*rw_lock_);
173  if (!handle)
174    return -1;
175
176  if (id_ != NULL) {
177    if (managed_file_handle_)
178      fclose(id_);
179    else
180      return -1;
181  }
182
183  id_ = handle;
184  managed_file_handle_ = manage_file;
185  read_only_ = read_only;
186  looping_ = loop;
187  open_ = true;
188  return 0;
189}
190
191int FileWrapperImpl::Read(void* buf, size_t length) {
192  WriteLockScoped write(*rw_lock_);
193  if (id_ == NULL)
194    return -1;
195
196  size_t bytes_read = fread(buf, 1, length, id_);
197  if (bytes_read != length && !looping_) {
198    CloseFileImpl();
199  }
200  return static_cast<int>(bytes_read);
201}
202
203int FileWrapperImpl::WriteText(const char* format, ...) {
204  WriteLockScoped write(*rw_lock_);
205  if (format == NULL)
206    return -1;
207
208  if (read_only_)
209    return -1;
210
211  if (id_ == NULL)
212    return -1;
213
214  va_list args;
215  va_start(args, format);
216  int num_chars = vfprintf(id_, format, args);
217  va_end(args);
218
219  if (num_chars >= 0) {
220    return num_chars;
221  } else {
222    CloseFileImpl();
223    return -1;
224  }
225}
226
227bool FileWrapperImpl::Write(const void* buf, size_t length) {
228  WriteLockScoped write(*rw_lock_);
229  if (buf == NULL)
230    return false;
231
232  if (read_only_)
233    return false;
234
235  if (id_ == NULL)
236    return false;
237
238  // Check if it's time to stop writing.
239  if (max_size_in_bytes_ > 0 &&
240      (size_in_bytes_ + length) > max_size_in_bytes_) {
241    FlushImpl();
242    return false;
243  }
244
245  size_t num_bytes = fwrite(buf, 1, length, id_);
246  if (num_bytes > 0) {
247    size_in_bytes_ += num_bytes;
248    return true;
249  }
250
251  CloseFileImpl();
252  return false;
253}
254
255int FileWrapperImpl::CloseFileImpl() {
256  if (id_ != NULL) {
257    if (managed_file_handle_)
258      fclose(id_);
259    id_ = NULL;
260  }
261  memset(file_name_utf8_, 0, kMaxFileNameSize);
262  open_ = false;
263  return 0;
264}
265
266int FileWrapperImpl::FlushImpl() {
267  if (id_ != NULL) {
268    return fflush(id_);
269  }
270  return -1;
271}
272
273int FileWrapper::Rewind() {
274  RTC_DCHECK(false);
275  return -1;
276}
277
278}  // namespace webrtc
279