Win32: handle file paths of arbitrary length (in file system code)
[vlc.git] / src / win32 / filesystem.c
1 /*****************************************************************************
2  * filesystem.c: Windows file system helpers
3  *****************************************************************************
4  * Copyright (C) 2005-2006 the VideoLAN team
5  * Copyright © 2005-2008 Rémi Denis-Courmont
6  *
7  * Authors: Rémi Denis-Courmont <rem # videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <vlc_common.h>
32 #include <vlc_charset.h>
33 #include <vlc_fs.h>
34 #include "libvlc.h" /* vlc_mkdir */
35
36 #include <assert.h>
37
38 #include <stdio.h>
39 #include <errno.h>
40 #include <sys/types.h>
41 #include <dirent.h>
42 #include <sys/stat.h>
43 #include <fcntl.h>
44 #include <winsock2.h>
45 #ifndef UNDER_CE
46 # include <direct.h>
47 #else
48 # include <tchar.h>
49 #endif
50
51 static wchar_t *widen_path (const char *path)
52 {
53     wchar_t *wpath;
54
55     errno = 0;
56     wpath = ToWide (path);
57     if (wpath == NULL)
58     {
59         if (errno == 0)
60             errno = ENOENT;
61         return NULL;
62     }
63     return wpath;
64 }
65
66 #define CONVERT_PATH(path, wpath, err) \
67     wchar_t *wpath = wide_path(path); \
68     if (wpath == NULL) return (err)
69
70
71 int vlc_open (const char *filename, int flags, ...)
72 {
73     unsigned int mode = 0;
74     va_list ap;
75
76     va_start (ap, flags);
77     if (flags & O_CREAT)
78         mode = va_arg (ap, unsigned int);
79     va_end (ap);
80
81 #ifdef UNDER_CE
82     /*_open translates to wchar internally on WinCE*/
83     return _open (filename, flags, mode);
84 #else
85     /*
86      * open() cannot open files with non-“ANSI” characters on Windows.
87      * We use _wopen() instead. Same thing for mkdir() and stat().
88      */
89     wchar_t *wpath = widen_path (filename);
90     if (wpath == NULL)
91         return -1;
92
93     int fd = _wopen (wpath, flags, mode);
94     free (wpath);
95     return fd;
96 #endif
97 }
98
99 int vlc_openat (int dir, const char *filename, int flags, ...)
100 {
101     (void) dir; (void) filename; (void) flags;
102     errno = ENOSYS;
103     return -1;
104 }
105
106 int vlc_mkdir( const char *dirname, mode_t mode )
107 {
108 #if defined (UNDER_CE)
109     (void) mode;
110     /* mkdir converts internally to wchar */
111     return _mkdir(dirname);
112 #else
113     wchar_t *wpath = widen_path (dirname);
114     if (wpath == NULL)
115         return -1;
116
117     int ret = _wmkdir (wpath);
118     free (wpath);
119     (void) mode;
120     return ret;
121 #endif
122 }
123
124 char *vlc_getcwd (void)
125 {
126     wchar_t *wdir = _wgetcwd (NULL, 0);
127     if (wdir == NULL)
128         return NULL;
129
130     char *dir = FromWide (wdir);
131     free (wdir);
132     return dir;
133 }
134
135 /* Under Windows, these wrappers return the list of drive letters
136  * when called with an empty argument or just '\'. */
137 typedef struct vlc_DIR
138 {
139     _WDIR *wdir; /* MUST be first, see <vlc_fs.h> */
140     union
141     {
142         DWORD drives;
143         bool insert_dot_dot;
144     } u;
145 } vlc_DIR;
146
147
148 DIR *vlc_opendir (const char *dirname)
149 {
150     wchar_t *wpath = widen_path (dirname);
151     if (wpath == NULL)
152         return NULL;
153
154     vlc_DIR *p_dir = malloc (sizeof (*p_dir));
155     if (unlikely(p_dir == NULL))
156     {
157         free(wpath);
158         return NULL;
159     }
160
161     if (wpath[0] == L'\0' || (wcscmp (wpath, L"\\") == 0))
162     {
163         free (wpath);
164         /* Special mode to list drive letters */
165         p_dir->wdir = NULL;
166 #ifdef UNDER_CE
167         p_dir->u.drives = 1;
168 #else
169         p_dir->u.drives = GetLogicalDrives ();
170 #endif
171         return (void *)p_dir;
172     }
173
174     assert (wpath[0]); // wpath[1] is defined
175     p_dir->u.insert_dot_dot = !wcscmp (wpath + 1, L":\\");
176
177     _WDIR *wdir = _wopendir (wpath);
178     free (wpath);
179     if (wdir == NULL)
180     {
181         free (p_dir);
182         return NULL;
183     }
184     p_dir->wdir = wdir;
185     return (void *)p_dir;
186 }
187
188 char *vlc_readdir (DIR *dir)
189 {
190     vlc_DIR *p_dir = (vlc_DIR *)dir;
191
192     if (p_dir->wdir == NULL)
193     {
194         /* Drive letters mode */
195         DWORD drives = p_dir->u.drives;
196         if (drives == 0)
197             return NULL; /* end */
198 #ifdef UNDER_CE
199         p_dir->u.drives = 0;
200         return strdup ("\\");
201 #else
202         unsigned int i;
203         for (i = 0; !(drives & 1); i++)
204             drives >>= 1;
205         p_dir->u.drives &= ~(1UL << i);
206         assert (i < 26);
207
208         char *ret;
209         if (asprintf (&ret, "%c:\\", 'A' + i) == -1)
210             return NULL;
211         return ret;
212 #endif
213     }
214
215     if (p_dir->u.insert_dot_dot)
216     {
217         /* Adds "..", gruik! */
218         p_dir->u.insert_dot_dot = false;
219         return strdup ("..");
220     }
221
222     struct _wdirent *ent = _wreaddir (p_dir->wdir);
223     if (ent == NULL)
224         return NULL;
225     return FromWide (ent->d_name);
226 }
227
228 int vlc_stat (const char *filename, struct stat *buf)
229 {
230 #ifdef UNDER_CE
231     /* _stat translates to wchar internally on WinCE */
232     return _stat (filename, buf);
233 #else
234     wchar_t *wpath = widen_path (filename);
235     if (wpath == NULL)
236         return -1;
237
238     int ret = _wstati64 (wpath, buf);
239     free (wpath);
240     return ret;
241 #endif
242 }
243
244 int vlc_lstat (const char *filename, struct stat *buf)
245 {
246     return vlc_stat (filename, buf);
247 }
248
249 int vlc_unlink (const char *filename)
250 {
251 #ifdef UNDER_CE
252     /*_open translates to wchar internally on WinCE*/
253     return _unlink( filename );
254 #else
255     wchar_t *wpath = widen_path (filename);
256     if (wpath == NULL)
257         return -1;
258
259     int ret = _wunlink (wpath);
260     free (wpath);
261     return ret;
262 #endif
263 }
264
265 int vlc_rename (const char *oldpath, const char *newpath)
266 {
267     int ret = -1;
268
269     wchar_t *wold = widen_path (oldpath), *wnew = widen_path (newpath);
270     if (wold == NULL || wnew == NULL)
271         goto out;
272
273 # ifdef UNDER_CE
274     /* FIXME: errno support */
275     if (MoveFileW (wold, wnew))
276         ret = 0;
277 #else
278     if (_wrename (wold, wnew) && (errno == EACCES || errno == EEXIST))
279     {   /* Windows does not allow atomic file replacement */
280         if (_wremove (wnew))
281         {
282             errno = EACCES; /* restore errno */
283             goto out;
284         }
285         if (_wrename (wold, wnew))
286             goto out;
287     }
288     ret = 0;
289 #endif
290 out:
291     free (wnew);
292     free (wold);
293     return ret;
294 }
295
296 int vlc_dup (int oldfd)
297 {
298 #ifdef UNDER_CE
299     (void) oldfd;
300     errno = ENOSYS;
301     return -1;
302 #else
303     return dup (oldfd);
304 #endif
305 }
306
307 int vlc_pipe (int fds[2])
308 {
309 #ifdef UNDER_CE
310     (void) fds;
311     errno = ENOSYS;
312     return -1;
313 #else
314     return _pipe (fds, 32768, O_BINARY);
315 #endif
316 }
317
318 #include <vlc_network.h>
319
320 int vlc_socket (int pf, int type, int proto, bool nonblock)
321 {
322     int fd = socket (pf, type, proto);
323     if (fd == -1)
324         return -1;
325
326     if (nonblock)
327         ioctlsocket (fd, FIONBIO, &(unsigned long){ 1 });
328     return fd;
329 }
330
331 int vlc_accept (int lfd, struct sockaddr *addr, socklen_t *alen, bool nonblock)
332 {
333     int fd = accept (lfd, addr, alen);
334     if (fd != -1 && nonblock)
335         ioctlsocket (fd, FIONBIO, &(unsigned long){ 1 });
336     return fd;
337 }