1 /* $Id$ */
2 /*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20 #include <pj/file_io.h>
21 #include <pj/unicode.h>
22 #include <pj/errno.h>
23 #include <pj/assert.h>
24 #include <pj/string.h>
25
26 #include <windows.h>
27
28 #ifndef INVALID_SET_FILE_POINTER
29 # define INVALID_SET_FILE_POINTER ((DWORD)-1)
30 #endif
31
set_file_pointer(pj_oshandle_t fd,pj_off_t offset,pj_off_t * newPos,DWORD dwMoveMethod)32 static pj_status_t set_file_pointer(pj_oshandle_t fd,
33 pj_off_t offset,
34 pj_off_t* newPos,
35 DWORD dwMoveMethod)
36 {
37 #if defined(PJ_WIN32_WINPHONE8) && PJ_WIN32_WINPHONE8
38 LARGE_INTEGER liDistance, liNewPos;
39
40 liDistance.QuadPart = offset;
41 if (!SetFilePointerEx(fd, liDistance, &liNewPos, dwMoveMethod)) {
42 return PJ_RETURN_OS_ERROR(GetLastError());
43 }
44 *newPos = liNewPos.QuadPart;
45 #else
46 DWORD dwNewPos;
47 LONG hi32;
48
49 hi32 = (LONG)(offset >> 32);
50
51 dwNewPos = SetFilePointer(fd, (long)offset, &hi32, dwMoveMethod);
52 if (dwNewPos == (DWORD)INVALID_SET_FILE_POINTER) {
53 DWORD dwStatus = GetLastError();
54 if (dwStatus != 0)
55 return PJ_RETURN_OS_ERROR(dwStatus);
56 /* dwNewPos actually is not an error. */
57 }
58 *newPos = hi32;
59 *newPos = (*newPos << 32) + dwNewPos;
60 #endif
61
62 return PJ_SUCCESS;
63 }
64
65 /**
66 * Check for end-of-file condition on the specified descriptor.
67 *
68 * @param fd The file descriptor.
69 * @param access The desired access.
70 *
71 * @return Non-zero if file is EOF.
72 */
73 PJ_DECL(pj_bool_t) pj_file_eof(pj_oshandle_t fd,
74 enum pj_file_access access);
75
76
pj_file_open(pj_pool_t * pool,const char * pathname,unsigned flags,pj_oshandle_t * fd)77 PJ_DEF(pj_status_t) pj_file_open( pj_pool_t *pool,
78 const char *pathname,
79 unsigned flags,
80 pj_oshandle_t *fd)
81 {
82 PJ_DECL_UNICODE_TEMP_BUF(wpathname, 256)
83 HANDLE hFile;
84 DWORD dwDesiredAccess = 0;
85 DWORD dwShareMode = 0;
86 DWORD dwCreationDisposition = 0;
87 DWORD dwFlagsAndAttributes = 0;
88
89 PJ_UNUSED_ARG(pool);
90
91 PJ_ASSERT_RETURN(pathname!=NULL, PJ_EINVAL);
92
93 if ((flags & PJ_O_WRONLY) == PJ_O_WRONLY) {
94 dwDesiredAccess |= GENERIC_WRITE;
95 if ((flags & PJ_O_APPEND) == PJ_O_APPEND) {
96 #if !defined(PJ_WIN32_WINCE) || !PJ_WIN32_WINCE
97 /* FILE_APPEND_DATA is invalid on WM2003 and WM5, but it seems
98 * to be working on WM6. All are tested on emulator though.
99 * Removing this also seem to work (i.e. data is appended), so
100 * I guess this flag is "optional".
101 * See http://trac.pjsip.org/repos/ticket/825
102 */
103 dwDesiredAccess |= FILE_APPEND_DATA;
104 #endif
105 dwCreationDisposition |= OPEN_ALWAYS;
106 } else {
107 dwDesiredAccess &= ~(FILE_APPEND_DATA);
108 dwCreationDisposition |= CREATE_ALWAYS;
109 }
110 }
111 if ((flags & PJ_O_RDONLY) == PJ_O_RDONLY) {
112 dwDesiredAccess |= GENERIC_READ;
113 if (flags == PJ_O_RDONLY)
114 dwCreationDisposition |= OPEN_EXISTING;
115 }
116
117 if (dwDesiredAccess == 0) {
118 pj_assert(!"Invalid file open flags");
119 return PJ_EINVAL;
120 }
121
122 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
123
124 dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
125
126 #if defined(PJ_WIN32_WINPHONE8) && PJ_WIN32_WINPHONE8
127 hFile = CreateFile2(PJ_STRING_TO_NATIVE(pathname,
128 wpathname, sizeof(wpathname)),
129 dwDesiredAccess, dwShareMode, dwCreationDisposition,
130 NULL);
131 #else
132 hFile = CreateFile(PJ_STRING_TO_NATIVE(pathname,
133 wpathname, sizeof(wpathname)),
134 dwDesiredAccess, dwShareMode, NULL,
135 dwCreationDisposition, dwFlagsAndAttributes, NULL);
136 #endif
137
138 if (hFile == INVALID_HANDLE_VALUE) {
139 DWORD lastErr = GetLastError();
140 *fd = 0;
141 return PJ_RETURN_OS_ERROR(lastErr);
142 }
143
144 if ((flags & PJ_O_APPEND) == PJ_O_APPEND) {
145 pj_status_t status;
146
147 status = pj_file_setpos(hFile, 0, PJ_SEEK_END);
148 if (status != PJ_SUCCESS) {
149 pj_file_close(hFile);
150 return status;
151 }
152 }
153
154 *fd = hFile;
155 return PJ_SUCCESS;
156 }
157
pj_file_close(pj_oshandle_t fd)158 PJ_DEF(pj_status_t) pj_file_close(pj_oshandle_t fd)
159 {
160 if (CloseHandle(fd)==0)
161 return PJ_RETURN_OS_ERROR(GetLastError());
162 return PJ_SUCCESS;
163 }
164
pj_file_write(pj_oshandle_t fd,const void * data,pj_ssize_t * size)165 PJ_DEF(pj_status_t) pj_file_write( pj_oshandle_t fd,
166 const void *data,
167 pj_ssize_t *size)
168 {
169 BOOL rc;
170 DWORD bytesWritten;
171
172 rc = WriteFile(fd, data, (DWORD)*size, &bytesWritten, NULL);
173 if (!rc) {
174 *size = -1;
175 return PJ_RETURN_OS_ERROR(GetLastError());
176 }
177
178 *size = bytesWritten;
179 return PJ_SUCCESS;
180 }
181
pj_file_read(pj_oshandle_t fd,void * data,pj_ssize_t * size)182 PJ_DEF(pj_status_t) pj_file_read( pj_oshandle_t fd,
183 void *data,
184 pj_ssize_t *size)
185 {
186 BOOL rc;
187 DWORD bytesRead;
188
189 rc = ReadFile(fd, data, (DWORD)*size, &bytesRead, NULL);
190 if (!rc) {
191 *size = -1;
192 return PJ_RETURN_OS_ERROR(GetLastError());
193 }
194
195 *size = bytesRead;
196 return PJ_SUCCESS;
197 }
198
199 /*
200 PJ_DEF(pj_bool_t) pj_file_eof(pj_oshandle_t fd, enum pj_file_access access)
201 {
202 BOOL rc;
203 DWORD dummy = 0, bytes;
204 DWORD dwStatus;
205
206 if ((access & PJ_O_RDONLY) == PJ_O_RDONLY) {
207 rc = ReadFile(fd, &dummy, 0, &bytes, NULL);
208 } else if ((access & PJ_O_WRONLY) == PJ_O_WRONLY) {
209 rc = WriteFile(fd, &dummy, 0, &bytes, NULL);
210 } else {
211 pj_assert(!"Invalid access");
212 return PJ_TRUE;
213 }
214
215 dwStatus = GetLastError();
216 if (dwStatus==ERROR_HANDLE_EOF)
217 return PJ_TRUE;
218
219 return 0;
220 }
221 */
222
pj_file_setpos(pj_oshandle_t fd,pj_off_t offset,enum pj_file_seek_type whence)223 PJ_DEF(pj_status_t) pj_file_setpos( pj_oshandle_t fd,
224 pj_off_t offset,
225 enum pj_file_seek_type whence)
226 {
227 DWORD dwMoveMethod;
228 pj_off_t newPos;
229
230 if (whence == PJ_SEEK_SET)
231 dwMoveMethod = FILE_BEGIN;
232 else if (whence == PJ_SEEK_CUR)
233 dwMoveMethod = FILE_CURRENT;
234 else if (whence == PJ_SEEK_END)
235 dwMoveMethod = FILE_END;
236 else {
237 pj_assert(!"Invalid whence in file_setpos");
238 return PJ_EINVAL;
239 }
240
241 if (set_file_pointer(fd, offset, &newPos, dwMoveMethod) != PJ_SUCCESS) {
242 return PJ_RETURN_OS_ERROR(GetLastError());
243 }
244
245 return PJ_SUCCESS;
246 }
247
pj_file_getpos(pj_oshandle_t fd,pj_off_t * pos)248 PJ_DEF(pj_status_t) pj_file_getpos( pj_oshandle_t fd,
249 pj_off_t *pos)
250 {
251 if (set_file_pointer(fd, 0, pos, FILE_CURRENT) != PJ_SUCCESS) {
252 return PJ_RETURN_OS_ERROR(GetLastError());
253 }
254
255 return PJ_SUCCESS;
256 }
257
pj_file_flush(pj_oshandle_t fd)258 PJ_DEF(pj_status_t) pj_file_flush(pj_oshandle_t fd)
259 {
260 BOOL rc;
261
262 rc = FlushFileBuffers(fd);
263
264 if (!rc) {
265 DWORD dwStatus = GetLastError();
266 if (dwStatus != 0)
267 return PJ_RETURN_OS_ERROR(dwStatus);
268 }
269
270 return PJ_SUCCESS;
271 }
272