1 #ifdef __CYGWIN__
2
3 #define SOCKET int
4 #define _get_osfhandle(a) a
5
6 #include <pthread.h>
7
8 #else
9
10 #include <winsock.h>
11
my_fd_zero(fd_set * f)12 void my_fd_zero( fd_set* f) { FD_ZERO( f); }
13
14 #endif
15
16 typedef fd_set type_fd_set;
std_fd_set(int fd,fd_set * f)17 void std_fd_set( int fd, fd_set * f) { FD_SET(fd, f); }
18
19 #include "win32\win32guts.h"
20 #ifndef _APRICOT_H_
21 #include "apricot.h"
22 #endif
23 #include "guts.h"
24 #include "Component.h"
25 #include "File.h"
26
my_fd_set(HANDLE fd,type_fd_set * f)27 void my_fd_set( HANDLE fd, type_fd_set * f) { std_fd_set( PTR2UV(fd), f); }
28
29 #define var (( PFile) self)->
30 #define sys (( PDrawableData)(( PComponent) self)-> sysData)->
31 #define dsys( view) (( PDrawableData)(( PComponent) view)-> sysData)->
32
33 #ifdef __cplusplus
34 extern "C" {
35 #endif
36
37 #ifndef __CYGWIN__
38
39 #undef select
40 #undef fd_set
41 #undef FD_ZERO
42 #define FD_ZERO my_fd_zero
43 #undef FD_SET
44 #define FD_SET my_fd_set
45
46 #endif
47
48 static Bool socketThreadStarted = false;
49 static Bool socketSetChanged = false;
50 static struct timeval socketTimeout = {0, 200000};
51 static char socketErrBuf [ 256];
52
53 static fd_set socketSet1[3];
54 static fd_set socketSet2[3];
55 static int socketCommands[3] = { feRead, feWrite, feException};
56
57 void
58 #ifdef __CYGWIN__
59 *
60 #endif
socket_select(void * dummy)61 socket_select( void *dummy)
62 {
63 int count;
64 while ( !appDead) {
65 if ( socketSetChanged) {
66 // updating handles
67 int i;
68 if ( WaitForSingleObject( guts. socketMutex, INFINITE) != WAIT_OBJECT_0) {
69 strcpy( socketErrBuf, "Failed to obtain socket mutex ownership for thread #2");
70 PostThreadMessage( guts. mainThreadId, WM_CROAK, 1, ( LPARAM) &socketErrBuf);
71 break;
72 }
73 for ( i = 0; i < 3; i++)
74 memcpy( socketSet1+i, socketSet2+i, sizeof( fd_set));
75 socketSetChanged = false;
76 ReleaseMutex( guts. socketMutex);
77 }
78
79 // calling select()
80 #ifndef __CYGWIN__
81 count = socketSet1[0]. fd_count + socketSet1[1]. fd_count + socketSet1[2]. fd_count;
82 #else
83 count = 0;
84 {
85 int i,j;
86 for ( i = 0; i < FD_SETSIZE; i++)
87 for ( j = 0; j < 3; i++)
88 if ( FD_ISSET( i, socketSet1+j)) {
89 count++;
90 goto END;
91 }
92 END:;
93 }
94 #endif
95 if ( count > 0) {
96 int i, j, result = select( FD_SETSIZE-1, &socketSet1[0], &socketSet1[1], &socketSet1[2], &socketTimeout);
97 socketSetChanged = true;
98 if ( result == 0) continue;
99 if ( result < 0) {
100 int err;
101 #ifndef __CYGWIN__
102 if (( err = WSAGetLastError()) == WSAENOTSOCK)
103 #else
104 if (( err = errno) == EBADF)
105 #endif
106 {
107 // possibly some socket was closed
108 guts. socketPostSync = 1;
109 PostThreadMessage( guts. mainThreadId, WM_SOCKET_REHASH, 0, 0);
110 while( guts. socketPostSync) Sleep(1);
111 } else {
112 // some error
113 char * msg;
114 #ifndef __CYGWIN__
115 msg = err_msg( err, socketErrBuf);
116 #else
117 strncpy( msg = socketErrBuf, strerror(err), 255);
118 socketErrBuf[255] = 0;
119 #endif
120 PostThreadMessage( guts. mainThreadId, WM_CROAK, 0, (LPARAM) msg);
121 }
122 continue;
123 }
124 // posting select() results
125 for ( j = 0; j < 3; j++)
126 #ifndef __CYGWIN__
127 for ( i = 0; i < socketSet1[j]. fd_count; i++) {
128 #else
129 for ( i = 0; i < FD_SETSIZE; i++) {
130 if ( !FD_ISSET( i, socketSet1 + j)) continue;
131 #endif
132 guts. socketPostSync = 1;
133 PostThreadMessage( guts. mainThreadId, WM_SOCKET, socketCommands[j],
134 #ifndef __CYGWIN__
135 ( LPARAM) socketSet1[j]. fd_array[i]
136 #else
137 ( LPARAM) i
138 #endif
139 );
140 while( guts. socketPostSync) Sleep(1);
141 }
142 } else
143 // nothing to 'select', sleeping
144 Sleep( socketTimeout. tv_sec * 1000 + socketTimeout. tv_usec / 1000);
145 }
146
147 // if somehow failed, making restart possible
148 socketThreadStarted = false;
149 #ifdef __CYGWIN__
150 return NULL;
151 #endif
152 }
153
154
155 static void
156 reset_sockets( void)
157 {
158 int i;
159
160 // enter section
161 if ( socketThreadStarted) {
162 if ( WaitForSingleObject( guts. socketMutex, INFINITE) != WAIT_OBJECT_0)
163 croak("Failed to obtain socket mutex ownership for thread #1");
164 }
165
166 // copying handles
167 for ( i = 0; i < 3; i++)
168 FD_ZERO( &socketSet2[i]);
169
170 for ( i = 0; i < guts. sockets. count; i++) {
171 Handle self = guts. sockets. items[i];
172 if ( var eventMask & feRead)
173 FD_SET( sys s. file. object, &socketSet2[0]);
174 if ( var eventMask & feWrite)
175 FD_SET( sys s. file. object, &socketSet2[1]);
176 if ( var eventMask & feException)
177 FD_SET( sys s. file. object, &socketSet2[2]);
178 }
179
180 socketSetChanged = true;
181
182 // leave section and start the thread, if needed
183 if ( !socketThreadStarted) {
184 if ( !( guts. socketMutex = CreateMutex( NULL, FALSE, NULL))) {
185 apiErr;
186 croak("Failed to create socket mutex object");
187 }
188 #ifndef __CYGWIN__
189 guts. socketThread = ( HANDLE) _beginthread( socket_select, 40960, NULL);
190 #else
191 pthread_create(( pthread_t*) &guts. socketThread, 0, socket_select, NULL);
192 #endif
193 socketThreadStarted = true;
194 } else
195 ReleaseMutex( guts. socketMutex);
196 }
197
198 void
199 socket_rehash( void)
200 {
201 int i;
202 for ( i = 0; i < guts. sockets. count; i++) {
203 Handle self = guts. sockets. items[i];
204 CFile( self)-> is_active( self, true);
205 }
206 }
207
208
209 Bool
210 apc_file_attach( Handle self)
211 {
212 int fhtype;
213 objCheck false;
214
215 if ( PFile(self)->fd > FD_SETSIZE ) return false;
216
217 if ( guts. socket_version == 0) {
218 int _data, _sz = sizeof( int);
219 (void)_data;
220 (void)_sz;
221 #ifdef __CYGWIN__
222 _sz = htons(80);
223 guts. socket_version = 2;
224 #else
225 #ifdef PERL_OBJECT // init perl socket library, if any
226 PL_piSock-> Htons( 80);
227 #else
228 win32_htons(80);
229 #endif
230 if ( getsockopt(( SOCKET) INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char*)&_data, &_sz) != 0)
231 guts. socket_version = -1; // no sockets available
232 else
233 #if PERL_PATCHLEVEL < 8
234 guts. socket_version = ( _data == SO_SYNCHRONOUS_NONALERT) ? 1 : 2;
235 #else
236 guts. socket_version = 1;
237 #endif
238
239 #endif
240 }
241
242 if ( SOCKETS_NONE)
243 return false;
244
245 sys s. file. object = SOCKETS_AS_HANDLES ?
246 (( SOCKETHANDLE) _get_osfhandle( var fd)) :
247 ((INT2PTR(SOCKETHANDLE, var fd)));
248
249 {
250 int _data, _sz = sizeof( int);
251 int result =
252 #ifndef __CYGWIN__
253 SOCKETS_AS_HANDLES ?
254 WSAAsyncSelect((SOCKET) sys s. file. object, (HWND) NULL, 0, 0) :
255 #endif
256 getsockopt(( SOCKET) sys s. file. object, SOL_SOCKET, SO_TYPE, (char*)&_data, &_sz);
257 if ( result != 0)
258 #ifndef __CYGWIN__
259 fhtype = ( WSAGetLastError() == WSAENOTSOCK) ? FHT_OTHER : FHT_SOCKET;
260 #else
261 fhtype = ( errno == EBADF) ? FHT_OTHER : FHT_SOCKET;
262 #endif
263 else
264 fhtype = FHT_SOCKET;
265 }
266
267 sys s. file. type = fhtype;
268
269 switch ( fhtype) {
270 case FHT_SOCKET:
271 list_add( &guts. sockets, self);
272 reset_sockets();
273 break;
274 default:
275 if ( guts. files. count == 0)
276 PostMessage( NULL, WM_FILE, 0, 0);
277 list_add( &guts. files, self);
278 break;
279 }
280
281 return true;
282 }
283
284 Bool
285 apc_file_detach( Handle self)
286 {
287 switch ( sys s. file. type) {
288 case FHT_SOCKET:
289 list_delete( &guts. sockets, self);
290 reset_sockets();
291 break;
292 default:
293 list_delete( &guts. files, self);
294 }
295 return true;
296 }
297
298 Bool
299 apc_file_change_mask( Handle self)
300 {
301 switch ( sys s. file. type) {
302 case FHT_SOCKET:
303 reset_sockets();
304 break;
305 default:;
306 }
307 return true;
308 }
309
310 PList
311 apc_getdir( const char *dirname, Bool is_utf8)
312 {
313 #ifdef __CYGWIN__
314 DIR *dh;
315 struct dirent *de;
316 PList dirlist = NULL;
317 char *type, *dname;
318 char path[ 2048];
319 struct stat s;
320
321 if ( *dirname == '/' && dirname[1] == '/') dirname++;
322 if ( strcmp( dirname, "/") == 0)
323 dname = "";
324 else
325 dname = ( char*) dirname;
326
327
328 if (( dh = opendir( dirname)) && (dirlist = plist_create( 50, 50))) {
329 while (( de = readdir( dh))) {
330 list_add( dirlist, (Handle)duplicate_string( de-> d_name));
331 snprintf( path, 2047, "%s/%s", dname, de-> d_name);
332 type = NULL;
333 if ( stat( path, &s) == 0) {
334 switch ( s. st_mode & S_IFMT) {
335 case S_IFIFO: type = "fifo"; break;
336 case S_IFCHR: type = "chr"; break;
337 case S_IFDIR: type = "dir"; break;
338 case S_IFBLK: type = "blk"; break;
339 case S_IFREG: type = "reg"; break;
340 case S_IFLNK: type = "lnk"; break;
341 case S_IFSOCK: type = "sock"; break;
342 }
343 }
344 if ( !type) type = "reg";
345 list_add( dirlist, (Handle)duplicate_string( type));
346 }
347 closedir( dh);
348 }
349 return dirlist;
350 #else
351 long len;
352 WCHAR scanname[(MAX_PATH+3)*2];
353 WIN32_FIND_DATAW FindData;
354 HANDLE fh;
355 WCHAR * dirname_w;
356
357 DWORD fattrs;
358 PList ret;
359 Bool wasDot = false, wasDotDot = false;
360
361 #define add_entry(file,info) { \
362 list_add( ret, ( Handle) duplicate_string(file)); \
363 list_add( ret, ( Handle) duplicate_string(info)); \
364 }
365
366 #define add_fentry { \
367 WideCharToMultiByte(CP_UTF8, 0, \
368 FindData.cFileName, -1, \
369 (LPSTR)scanname, sizeof(scanname), \
370 NULL, false); \
371 add_entry((char*) scanname, \
372 ( FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? DIR : FILE); \
373 if ( wcscmp( L".", FindData.cFileName) == 0) \
374 wasDot = true; \
375 else if ( wcscmp( L"..", FindData.cFileName) == 0) \
376 wasDotDot = true; \
377 }
378
379
380 #define DIR "dir"
381 #define FILE "reg"
382
383 dirname_w = is_utf8 ?
384 alloc_utf8_to_wchar( dirname, -1, NULL) :
385 alloc_ascii_to_wchar( dirname, -1);
386
387 len = wcslen(dirname_w);
388 if (len > MAX_PATH) {
389 free(dirname_w);
390 return NULL;
391 }
392
393 /* check to see if filename is a directory */
394 fattrs = GetFileAttributesW( dirname_w);
395 if ( fattrs == 0xFFFFFFFF || ( fattrs & FILE_ATTRIBUTE_DIRECTORY) == 0) {
396 free(dirname_w);
397 return NULL;
398 }
399
400 /* Create the search pattern */
401 wcscpy(scanname, dirname_w);
402 if (scanname[len-1] != '/' && scanname[len-1] != '\\')
403 scanname[len++] = '/';
404 scanname[len++] = '*';
405 scanname[len] = '\0';
406 free(dirname_w);
407
408 /* do the FindFirstFile call */
409 fh = FindFirstFileW(scanname, &FindData);
410 if (fh == INVALID_HANDLE_VALUE) {
411 /* FindFirstFile() fails on empty drives! */
412 if (GetLastError() != ERROR_FILE_NOT_FOUND)
413 return NULL;
414 ret = plist_create( 2, 16);
415 add_entry( ".", DIR);
416 add_entry( "..", DIR);
417 return ret;
418 }
419
420 ret = plist_create( 16, 16);
421 add_fentry;
422 while ( FindNextFileW(fh, &FindData))
423 add_fentry;
424 FindClose(fh);
425
426 if ( !wasDot)
427 add_entry( ".", DIR);
428 if ( !wasDotDot)
429 add_entry( "..", DIR);
430
431 #undef FILE
432 #undef DIR
433 return ret;
434 #endif
435 }
436
437 static WCHAR*
438 path2wchar(const char *name, Bool is_utf8, int * size)
439 {
440 WCHAR * text;
441 int xlen = -1;
442 if ( size == NULL ) size = &xlen;
443 if ( is_utf8 ) {
444 text = alloc_utf8_to_wchar( name, *size, size);
445 } else {
446 *size = strlen( name) + 1;
447 text = alloc_ascii_to_wchar( name, *size);
448 }
449 if ( !text ) errno = ENOMEM;
450 return text;
451 }
452
453 void
454 win32_set_errno(void)
455 {
456 /*
457 This isn't perfect, eg. Win32 returns ERROR_ACCESS_DENIED for
458 both permissions errors and if the source is a directory, while
459 POSIX wants EACCES and EPERM respectively.
460
461 Determined by experimentation on Windows 7 x64 SP1, since MS
462 don't document what error codes are returned.
463 */
464 switch (GetLastError()) {
465 case ERROR_BAD_NET_NAME:
466 case ERROR_BAD_NETPATH:
467 case ERROR_BAD_PATHNAME:
468 case ERROR_FILE_NOT_FOUND:
469 case ERROR_FILENAME_EXCED_RANGE:
470 case ERROR_INVALID_DRIVE:
471 case ERROR_PATH_NOT_FOUND:
472 errno = ENOENT;
473 break;
474 case ERROR_ALREADY_EXISTS:
475 errno = EEXIST;
476 break;
477 case ERROR_ACCESS_DENIED:
478 errno = EACCES;
479 break;
480 case ERROR_NOT_SAME_DEVICE:
481 errno = EXDEV;
482 break;
483 case ERROR_DISK_FULL:
484 errno = ENOSPC;
485 break;
486 case ERROR_NOT_ENOUGH_QUOTA:
487 errno = EDQUOT;
488 break;
489 default: /* ERROR_INVALID_FUNCTION - eg. on a FAT volume */
490 errno = EINVAL;
491 break;
492 }
493 }
494
495 typedef struct {
496 long position;
497 HANDLE handle;
498 WIN32_FIND_DATAW fd;
499 Bool error;
500 WCHAR path[MAX_PATH+3];
501 } Win32_Dirhandle;
502
503 int
504 apc_fs_access(const char *name, Bool is_utf8, int mode, Bool effective)
505 {
506 WCHAR *buf;
507 int ret;
508
509 if ( is_utf8 ) {
510 if ( !( buf = path2wchar(name, is_utf8, NULL)))
511 return false;
512 ret = _waccess(buf, mode);
513 free(buf);
514 } else
515 ret = access(name, mode);
516
517 return ret;
518 }
519
520 Bool
521 apc_fs_chdir(const char *path, Bool is_utf8 )
522 {
523 WCHAR *buf;
524 Bool ok;
525
526 if ( is_utf8 ) {
527 if ( !( buf = path2wchar(path, is_utf8, NULL)))
528 return false;
529 if ( !( ok = SetCurrentDirectoryW(buf)))
530 win32_set_errno();
531 free(buf);
532 } else {
533 if ( !( ok = SetCurrentDirectoryA(path)))
534 win32_set_errno();
535 }
536
537 return ok;
538 }
539
540 Bool
541 apc_fs_chmod( const char *path, Bool is_utf8, int mode)
542 {
543 WCHAR *buf;
544 Bool ok;
545
546 if ( is_utf8 ) {
547 if ( !( buf = path2wchar(path, is_utf8, NULL)))
548 return false;
549 ok = (_wchmod(buf, mode) == 0);
550 free(buf);
551 } else
552 ok = (chmod(path, mode) == 0);
553
554 return ok;
555 }
556
557 char *
558 alloc_wchar_to_utf8( WCHAR * src, int * len )
559 {
560 int xlen = -1, srclen;
561 char * ret;
562
563 if ( !len ) len = &xlen;
564 srclen = *len;
565
566 if (( *len = WideCharToMultiByte(CP_UTF8, 0, src, srclen, NULL, 0, NULL, false)) == 0 ) {
567 errno = EINVAL;
568 return NULL;
569 }
570 if ( !( ret = malloc( *len ))) {
571 errno = ENOMEM;
572 return NULL;
573 }
574 if ( WideCharToMultiByte(CP_UTF8, 0, src, srclen, ret, *len, NULL, false) == 0) {
575 free(ret);
576 errno = EINVAL;
577 return NULL;
578 }
579 return ret;
580 }
581
582 static char *
583 wstr2ascii( WCHAR * src, int * len, Bool fail_if_cannot )
584 {
585 char * ret;
586 int srclen = *len;
587 DWORD flags = 0;
588 #ifdef WC_ERR_INVALID_CHARS
589 if ( fail_if_cannot ) flags |= WC_ERR_INVALID_CHARS;
590 #endif
591 if (( *len = WideCharToMultiByte(CP_ACP, flags, src, srclen, NULL, 0, NULL, false)) == 0 )
592 return NULL;
593 if ( !( ret = malloc( *len )))
594 return NULL;
595 if ( WideCharToMultiByte(CP_ACP, flags, src, srclen, ret, *len, NULL, false) == 0) {
596 free(ret);
597 return NULL;
598 }
599 #ifndef WC_ERR_INVALID_CHARS
600 if ( fail_if_cannot ) {
601 int nq1 = 0, nq2 = 0, xlen;
602 char * ret2 = ret;
603 xlen = srclen;
604 while (xlen--) if ( *(src++) == L'?' ) nq1++;
605 xlen = srclen;
606 while (xlen--) if ( *(ret2++) == '?' ) nq2++;
607 if ( nq2 > nq1 ) {
608 free(ret);
609 return NULL;
610 }
611 }
612 #endif
613 return ret;
614 }
615
616 char *
617 apc_fs_from_local(const char * text, int * len)
618 {
619 char * ret;
620 WCHAR * buf;
621 if ( !( buf = alloc_ascii_to_wchar( text, *len)))
622 return NULL;
623 ret = alloc_wchar_to_utf8( buf, len );
624 free( buf );
625 return ret;
626 }
627
628
629 char *
630 apc_fs_to_local(const char * text, Bool fail_if_cannot, int * len)
631 {
632 WCHAR *buf;
633 char * ret;
634
635 if ( !( buf = path2wchar(text, true, len)))
636 return NULL;
637 ret = wstr2ascii( buf, len, fail_if_cannot );
638 free(buf);
639
640 return ret;
641 }
642
643 Bool
644 apc_fs_closedir( PDirHandleRec dh)
645 {
646 Bool ok;
647 Win32_Dirhandle *d = (Win32_Dirhandle*) dh-> handle;
648 ok = FindClose(d->handle);
649 free(d);
650 return ok;
651 }
652
653 char*
654 apc_fs_getcwd(void)
655 {
656 int i;
657 WCHAR fn[MAX_PATH+1];
658
659 if ( !GetCurrentDirectoryW(MAX_PATH+1, fn)) {
660 errno = EACCES;
661 return NULL;
662 }
663 for ( i = 0; i < MAX_PATH; i++) {
664 if ( fn[i] == 0 ) break;
665 if ( fn[i] == L'\\' ) fn[i] = L'/';
666 }
667 return alloc_wchar_to_utf8(fn, NULL);
668 }
669
670 char*
671 apc_fs_getenv(const char * varname, Bool is_utf8, Bool * do_free)
672 {
673 WCHAR * buf, e[32768];
674 Bool ok;
675
676 if ( !( buf = path2wchar(varname, is_utf8, NULL)))
677 return NULL;
678 ok = (GetEnvironmentVariableW(buf, e, sizeof(e)) > 0);
679 free(buf);
680 if ( !ok ) return NULL;
681
682 *do_free = true;
683 return alloc_wchar_to_utf8(e, NULL);
684 }
685
686 Bool
687 apc_fs_link( const char* oldname, Bool is_old_utf8, const char * newname, Bool is_new_utf8 )
688 {
689 WCHAR *buf1, *buf2;
690 Bool ok;
691
692 if ( !( buf1 = path2wchar(oldname, is_old_utf8, NULL)))
693 return false;
694 if ( !( buf2 = path2wchar(newname, is_new_utf8, NULL))) {
695 free(buf1);
696 return false;
697 }
698 if ( !( ok = ( CreateHardLinkW(buf2, buf1, NULL) == 0)))
699 win32_set_errno();
700 free(buf2);
701 free(buf1);
702
703 return ok;
704 }
705
706 Bool
707 apc_fs_mkdir( const char* path, Bool is_utf8, int mode)
708 {
709 WCHAR *buf;
710 Bool ok;
711
712 if ( !( buf = path2wchar(path, is_utf8, NULL)))
713 return false;
714 ok = (_wmkdir(buf) == 0);
715 if ( ok ) _wchmod(buf, mode);
716 free(buf);
717
718 return ok;
719 }
720
721 Bool
722 apc_fs_opendir( const char* path, PDirHandleRec dh)
723 {
724 WCHAR * buf;
725 DWORD fattrs;
726 int len;
727 Win32_Dirhandle * d;
728
729 if ( !( buf = path2wchar( path, dh-> is_utf8, NULL ))) {
730 errno = ENOMEM;
731 return false;
732 }
733
734 len = wcslen(buf);
735 if (len > MAX_PATH) {
736 free(buf);
737 errno = ENOMEM;
738 return false;
739 }
740 fattrs = GetFileAttributesW( buf);
741 if ( fattrs == 0xFFFFFFFF ) {
742 free(buf);
743 errno = ENOENT;
744 return false;
745 }
746 if (( fattrs & FILE_ATTRIBUTE_DIRECTORY) == 0) {
747 free(buf);
748 errno = ENOTDIR;
749 return false;
750 }
751
752 if ( !( dh-> handle = malloc(sizeof(Win32_Dirhandle)))) {
753 free(buf);
754 errno = ENOMEM;
755 return false;
756 }
757 d = ( Win32_Dirhandle*) dh->handle;
758 bzero( d, sizeof( Win32_Dirhandle ));
759
760 wcscpy(d->path, buf);
761 if (d->path[len-1] != '/' && d->path[len-1] != '\\')
762 d->path[len++] = '/';
763 d->path[len++] = '*';
764 d->path[len] = '\0';
765 free(buf);
766
767 d->handle = FindFirstFileW( d->path, &d->fd);
768 if ( d->handle == INVALID_HANDLE_VALUE ) {
769 d-> error = true;
770 win32_set_errno();
771 return false;
772 }
773 d-> position = 0;
774 return true;
775 }
776
777 int
778 apc_fs_open_file( const char* path, Bool is_utf8, int flags, int mode)
779 {
780 WCHAR *buf;
781 int f;
782
783 if ( is_utf8 ) {
784 if ( !( buf = path2wchar(path, is_utf8, NULL)))
785 return false;
786 f = _wopen(buf, flags, mode);
787 free(buf);
788 } else
789 f = open(path, flags, mode);
790
791 return f;
792 }
793
794 Bool
795 apc_fs_readdir( PDirHandleRec dh, char * entry)
796 {
797 Win32_Dirhandle *d = (Win32_Dirhandle*) dh-> handle;
798 if ( d-> error )
799 return false;
800 if ( d-> position > 0 ) {
801 if ( !FindNextFileW(d->handle, &d->fd)) {
802 d-> error = true;
803 return false;
804 }
805 }
806 d-> position++;
807 WideCharToMultiByte(CP_UTF8, 0, d->fd.cFileName, -1, entry, MAX_PATH, NULL, false);
808 return true;
809 }
810
811 Bool
812 apc_fs_rename( const char* oldname, Bool is_old_utf8, const char * newname, Bool is_new_utf8 )
813 {
814 Bool ok;
815 WCHAR *buf1, *buf2;
816 if ( !( buf1 = path2wchar(oldname, is_old_utf8, NULL)))
817 return false;
818 if ( !( buf2 = path2wchar(newname, is_new_utf8, NULL))) {
819 free(buf1);
820 return false;
821 }
822 ok = ( _wrename(buf1, buf2) == 0);
823 free(buf2);
824 free(buf1);
825 return ok;
826 }
827
828 Bool
829 apc_fs_rewinddir( PDirHandleRec dh )
830 {
831 Win32_Dirhandle *d = (Win32_Dirhandle*) dh-> handle;
832
833 d-> error = false;
834 FindClose(d-> handle);
835 d->handle = FindFirstFileW( d->path, &d->fd);
836 if ( d->handle == INVALID_HANDLE_VALUE ) {
837 d-> error = true;
838 win32_set_errno();
839 return false;
840 }
841 d-> position = 0;
842 return true;
843 }
844
845 Bool
846 apc_fs_rmdir( const char* path, Bool is_utf8 )
847 {
848 WCHAR *buf;
849 Bool ok;
850
851 if ( is_utf8 ) {
852 if ( !( buf = path2wchar(path, is_utf8, NULL)))
853 return false;
854 ok = (_wrmdir(buf) == 0);
855 free(buf);
856 } else
857 ok = (rmdir(path) == 0);
858
859 return ok;
860 }
861
862 Bool
863 apc_fs_seekdir( PDirHandleRec dh, long position )
864 {
865 Win32_Dirhandle *d = (Win32_Dirhandle*) dh-> handle;
866
867 if ( position == d->position ) return true;
868 if ( position < d->position || d->error ) {
869 if ( !apc_fs_rewinddir(dh))
870 return false;
871 }
872
873 while ( position != d->position ) {
874 char buf[PATH_MAX_UTF8];
875 if ( !apc_fs_readdir(dh, buf))
876 return false;
877 }
878
879 return true;
880 }
881
882 Bool
883 apc_fs_stat(const char *name, Bool is_utf8, Bool link, PStatRec statrec)
884 {
885 WCHAR *buf;
886 struct _stat statbuf;
887 int sz = -1, osz;
888
889 if ( !( buf = path2wchar(name, is_utf8, &sz)))
890 return false;
891 osz = sz;
892
893 /* from win32_stat:
894 stat() is buggy with a trailing slashes, except for the root directory of a drive:
895 remove additional trailing slashes */
896 while ( sz > 2 && ( buf[sz - 2] == L'\\' || buf[sz - 2] == L'/') ) {
897 buf[sz - 2] = 0;
898 sz--;
899 }
900 /* add back slash if we otherwise end up with just a drive letter */
901 if ( sz == 3 && isALPHA(buf[0]) && buf[1] == L':' ) {
902 if ( osz == sz ) {
903 WCHAR * buf2;
904 buf2 = realloc(buf, sz * 2 + 2);
905 if ( !buf2 ) {
906 free(buf);
907 errno = ENOMEM;
908 return false;
909 }
910 buf = buf2;
911 }
912 buf[sz++ - 1] = L'\\';
913 buf[sz - 1] = 0;
914 }
915 if ( _wstat(buf, &statbuf) < 0 ) {
916 free(buf);
917 return 0;
918 }
919 free(buf);
920
921 statrec-> dev = statbuf. st_dev;
922 statrec-> ino = statbuf. st_ino;
923 statrec-> mode = statbuf. st_mode;
924 statrec-> nlink = statbuf. st_nlink;
925 statrec-> uid = statbuf. st_uid;
926 statrec-> gid = statbuf. st_gid;
927 statrec-> rdev = statbuf. st_rdev;
928 statrec-> size = statbuf. st_size;
929 statrec-> blksize = -1;
930 statrec-> blocks = -1;
931 statrec-> atim = (float) statbuf.st_atime;
932 statrec-> mtim = (float) statbuf.st_mtime;
933 statrec-> ctim = (float) statbuf.st_ctime;
934 return 1;
935 }
936
937 long
938 apc_fs_telldir( PDirHandleRec dh )
939 {
940 Win32_Dirhandle *d = (Win32_Dirhandle*) dh-> handle;
941 return d->position;
942 }
943
944 Bool
945 apc_fs_unlink( const char* path, Bool is_utf8 )
946 {
947 WCHAR *buf;
948 Bool ok;
949
950 if ( is_utf8 ) {
951 if ( !( buf = path2wchar(path, is_utf8, NULL)))
952 return false;
953 ok = (_wunlink(buf) == 0);
954 free(buf);
955 } else
956 ok = (unlink(path) == 0);
957
958 return ok;
959 }
960
961 /* from win32/win32.c */
962
963 /* fix utime() so it works on directories in NT */
964 static Bool
965 filetime_from_time(PFILETIME pFileTime, float Time)
966 {
967 time_t sec = (time_t) Time;
968 struct tm *pTM = localtime(&sec);
969 SYSTEMTIME SystemTime;
970 FILETIME LocalTime;
971
972 if (pTM == NULL)
973 return false;
974
975 SystemTime.wYear = pTM->tm_year + 1900;
976 SystemTime.wMonth = pTM->tm_mon + 1;
977 SystemTime.wDay = pTM->tm_mday;
978 SystemTime.wHour = pTM->tm_hour;
979 SystemTime.wMinute = pTM->tm_min;
980 SystemTime.wSecond = pTM->tm_sec;
981 SystemTime.wMilliseconds = ( Time - (int) Time ) * 1000;
982
983 return SystemTimeToFileTime(&SystemTime, &LocalTime) &&
984 LocalFileTimeToFileTime(&LocalTime, pFileTime);
985 }
986
987 Bool
988 apc_fs_setenv(const char * varname, Bool is_name_utf8, const char * value, Bool is_value_utf8)
989 {
990 WCHAR *buf1, *buf2;
991 Bool ok = false;
992
993 if ( !( buf1 = path2wchar(varname, is_name_utf8, NULL)))
994 return false;
995 if ( !( buf2 = path2wchar(value, is_value_utf8, NULL))) {
996 free(buf1);
997 return false;
998 }
999
1000 ok = (SetEnvironmentVariableW(buf1, buf2) != 0);
1001
1002 free(buf2);
1003 free(buf1);
1004
1005 return ok;
1006 }
1007
1008 static Bool
1009 win32_utimes(float atime, float mtime, WCHAR * filename)
1010 {
1011 Bool ok = false;
1012 HANDLE handle;
1013 FILETIME ftCreate;
1014 FILETIME ftAccess;
1015 FILETIME ftWrite;
1016
1017 /* This will (and should) still fail on readonly files */
1018 handle = CreateFileW(filename, GENERIC_READ | GENERIC_WRITE,
1019 FILE_SHARE_READ | FILE_SHARE_DELETE, NULL,
1020 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
1021 if (handle == INVALID_HANDLE_VALUE) {
1022 errno = EINVAL;
1023 return false;
1024 }
1025
1026 if (!GetFileTime(handle, &ftCreate, &ftAccess, &ftWrite)) {
1027 win32_set_errno();
1028 goto EXIT;
1029 }
1030 if (
1031 !filetime_from_time(&ftAccess, atime) ||
1032 !filetime_from_time(&ftWrite, mtime)
1033 ) {
1034 errno = EINVAL;
1035 goto EXIT;
1036 }
1037 if ( !SetFileTime(handle, &ftCreate, &ftAccess, &ftWrite)) {
1038 win32_set_errno();
1039 goto EXIT;
1040 }
1041
1042 CloseHandle(handle);
1043 ok = true;
1044 EXIT:
1045 return ok;
1046 }
1047
1048
1049 Bool
1050 apc_fs_utime( double atime, double mtime, const char* path, Bool is_utf8 )
1051 {
1052 WCHAR *buf;
1053 Bool ok;
1054
1055 if ( !( buf = path2wchar(path, is_utf8, NULL)))
1056 return false;
1057 ok = win32_utimes(atime, mtime, buf);
1058 free(buf);
1059
1060 return ok;
1061 }
1062
1063
1064
1065 #ifdef __cplusplus
1066 }
1067 #endif
1068