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