2 Copyright (C) 2001-2009, Parrot Foundation.
7 src/io/win32.c - Win32 I/O utility functions
11 This file implements OS-specific I/O functions for Win32 platforms.
15 Win32 System Programming, 2nd Edition.
29 #include "parrot/parrot.h"
30 #include "pmc/pmc_filehandle.h"
31 #include "io_private.h"
35 /* HEADERIZER HFILE: include/parrot/io_win32.h */
36 /* HEADERIZER BEGIN: static */
37 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
39 static INTVAL
convert_flags_to_win32(
41 ARGOUT(DWORD
* fdwAccess
),
42 ARGOUT(DWORD
* fdwShareMode
),
43 ARGOUT(DWORD
* fdwCreate
))
44 __attribute__nonnull__(2)
45 __attribute__nonnull__(3)
46 __attribute__nonnull__(4)
47 FUNC_MODIFIES(* fdwAccess
)
48 FUNC_MODIFIES(* fdwShareMode
)
49 FUNC_MODIFIES(* fdwCreate
);
51 PARROT_WARN_UNUSED_RESULT
52 static INTVAL
io_is_tty_win32(PIOHANDLE fd
);
54 #define ASSERT_ARGS_convert_flags_to_win32 __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
55 PARROT_ASSERT_ARG(fdwAccess) \
56 , PARROT_ASSERT_ARG(fdwShareMode) \
57 , PARROT_ASSERT_ARG(fdwCreate))
58 #define ASSERT_ARGS_io_is_tty_win32 __attribute__unused__ int _ASSERT_ARGS_CHECK = (0)
59 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
60 /* HEADERIZER END: static */
68 =item C<static INTVAL convert_flags_to_win32(INTVAL flags, DWORD * fdwAccess,
69 DWORD * fdwShareMode, DWORD * fdwCreate)>
71 Convert to platform-specific bit open flags.
78 convert_flags_to_win32(INTVAL flags
, ARGOUT(DWORD
* fdwAccess
),
79 ARGOUT(DWORD
* fdwShareMode
), ARGOUT(DWORD
* fdwCreate
))
81 ASSERT_ARGS(convert_flags_to_win32
)
82 static DWORD dwDefaultShareMode
;
83 if (!dwDefaultShareMode
) {
85 osvi
.dwOSVersionInfoSize
= sizeof (osvi
);
87 if (osvi
.dwPlatformId
== VER_PLATFORM_WIN32_WINDOWS
) {
88 dwDefaultShareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
92 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
;
96 if ((flags
& (PIO_F_WRITE
| PIO_F_READ
)) == (PIO_F_WRITE
| PIO_F_READ
)) {
97 *fdwAccess
= GENERIC_WRITE
| GENERIC_READ
;
98 if (flags
& PIO_F_TRUNC
)
99 *fdwCreate
= CREATE_ALWAYS
;
101 *fdwCreate
= OPEN_ALWAYS
;
103 else if (flags
& PIO_F_WRITE
) {
104 *fdwAccess
= GENERIC_WRITE
;
105 if (flags
& PIO_F_TRUNC
)
106 *fdwCreate
= CREATE_ALWAYS
;
108 *fdwCreate
= OPEN_ALWAYS
;
110 else if (flags
& PIO_F_READ
) {
111 *fdwAccess
= GENERIC_READ
;
112 *fdwCreate
= OPEN_EXISTING
;
115 *fdwShareMode
= dwDefaultShareMode
;
116 if (flags
& PIO_F_APPEND
) {
117 /* dealt with specially in _write and _puts */
124 =item C<INTVAL Parrot_io_init_win32(PARROT_INTERP)>
126 Sets up the standard C<std*> IO handles.
133 Parrot_io_init_win32(PARROT_INTERP
)
135 ASSERT_ARGS(Parrot_io_init_win32
)
137 struct WSAData sockinfo
;
140 if ((h
= GetStdHandle(STD_INPUT_HANDLE
)) != INVALID_HANDLE_VALUE
) {
141 _PIO_STDIN(interp
) = Parrot_io_fdopen_win32(interp
, PMCNULL
, h
, PIO_F_READ
);
144 _PIO_STDIN(interp
) = PMCNULL
;
146 if ((h
= GetStdHandle(STD_OUTPUT_HANDLE
)) != INVALID_HANDLE_VALUE
) {
147 _PIO_STDOUT(interp
) = Parrot_io_fdopen_win32(interp
, PMCNULL
, h
, PIO_F_WRITE
);
150 _PIO_STDOUT(interp
) = PMCNULL
;
152 if ((h
= GetStdHandle(STD_ERROR_HANDLE
)) != INVALID_HANDLE_VALUE
) {
153 _PIO_STDERR(interp
) = Parrot_io_fdopen_win32(interp
, PMCNULL
, h
, PIO_F_WRITE
);
156 _PIO_STDERR(interp
) = PMCNULL
;
159 * no idea where or whether destroy it
161 ret
= WSAStartup(2, &sockinfo
);
163 fprintf(stderr
, "WSAStartup failed!!\n ErrorCode=%i\n\n",
172 =item C<INTVAL Parrot_io_getblksize_win32(PIOHANDLE fd)>
174 Returns C<PIO_BLKSIZE>.
181 Parrot_io_getblksize_win32(SHIM(PIOHANDLE fd
))
183 ASSERT_ARGS(Parrot_io_getblksize_win32
)
184 /* Hard coded for now */
190 =item C<PMC * Parrot_io_open_win32(PARROT_INTERP, PMC *filehandle, STRING *path,
193 Calls C<CreateFile()> to open C<*spath> with the Win32 translation of
200 PARROT_CAN_RETURN_NULL
202 Parrot_io_open_win32(PARROT_INTERP
, ARGMOD(PMC
*filehandle
),
203 ARGIN(STRING
*path
), INTVAL flags
)
205 ASSERT_ARGS(Parrot_io_open_win32
)
206 DWORD fAcc
, fShare
, fCreat
;
210 if ((Interp_flags_TEST(interp
, PARROT_DEBUG_FLAG
)) != 0) {
211 fprintf(stderr
, "Parrot_io_open_win32: %s\n", spath
);
214 if (flags
& PIO_F_PIPE
)
215 return Parrot_io_open_pipe_win32(interp
, filehandle
, path
, flags
);
217 if ((flags
& (PIO_F_WRITE
| PIO_F_READ
)) == 0)
220 /* Set open flags - <, >, >>, +<, +> */
221 /* add ? and ! for block/non-block */
222 if (convert_flags_to_win32(flags
, &fAcc
, &fShare
, &fCreat
) < 0)
225 /* Only files for now */
228 { /* enclosing scope for temporary C string */
229 char * const spath
= Parrot_str_to_cstring(interp
, path
);
230 fd
= CreateFile(spath
, fAcc
, fShare
, NULL
, fCreat
,
231 FILE_ATTRIBUTE_NORMAL
, NULL
);
232 Parrot_str_free_cstring(spath
);
234 if (fd
!= INVALID_HANDLE_VALUE
) {
236 if (PMC_IS_NULL(filehandle
))
237 io
= Parrot_io_new_pmc(interp
, flags
);
240 Parrot_io_set_flags(interp
, io
, flags
);
243 Parrot_io_set_os_handle(interp
, io
, fd
);
247 int err
= GetLastError();
258 =item C<PMC * Parrot_io_fdopen_win32(PARROT_INTERP, PMC *filehandle, PIOHANDLE
261 Returns a new C<PMC> with C<fd> as its file descriptor.
267 PARROT_WARN_UNUSED_RESULT
268 PARROT_CANNOT_RETURN_NULL
270 Parrot_io_fdopen_win32(PARROT_INTERP
, ARGMOD_NULLOK(PMC
*filehandle
),
271 PIOHANDLE fd
, INTVAL flags
)
273 ASSERT_ARGS(Parrot_io_fdopen_win32
)
276 if (io_is_tty_win32(fd
))
277 flags
|= PIO_F_CONSOLE
;
279 /* fdopened files are always shared */
280 flags
|= PIO_F_SHARED
;
282 if (PMC_IS_NULL(filehandle
))
283 io
= Parrot_io_new_pmc(interp
, flags
);
286 Parrot_io_set_flags(interp
, io
, flags
);
289 Parrot_io_set_os_handle(interp
, io
, fd
);
295 =item C<INTVAL Parrot_io_close_piohandle_win32(PARROT_INTERP, PIOHANDLE handle)>
297 Calls C<CloseHandle()> to close the given file descriptor. Returns 0 on
298 success, -1 on error.
305 Parrot_io_close_piohandle_win32(PARROT_INTERP
, PIOHANDLE handle
)
307 ASSERT_ARGS(Parrot_io_close_piohandle_win32
)
309 if (handle
== INVALID_HANDLE_VALUE
)
312 return CloseHandle(handle
) ? 0 : -1;
317 =item C<INTVAL Parrot_io_close_win32(PARROT_INTERP, PMC *filehandle)>
319 Calls C<CloseHandle()> to close C<*io>'s file descriptor.
326 Parrot_io_close_win32(PARROT_INTERP
, ARGMOD(PMC
*filehandle
))
328 ASSERT_ARGS(Parrot_io_close_win32
)
330 PIOHANDLE os_handle
= Parrot_io_get_os_handle(interp
, filehandle
);
331 if (os_handle
!= INVALID_HANDLE_VALUE
) {
332 int flags
= Parrot_io_get_flags(interp
, filehandle
);
333 if (CloseHandle(os_handle
) == 0)
334 result
= GetLastError();
335 Parrot_io_set_os_handle(interp
, filehandle
, INVALID_HANDLE_VALUE
);
337 if (flags
& PIO_F_PIPE
) {
338 INTVAL procid
= VTABLE_get_integer_keyed_int(interp
, filehandle
, 0);
339 HANDLE process
= (HANDLE
) procid
;
340 DWORD status
= WaitForSingleObject(process
, INFINITE
);
343 if (status
!= WAIT_FAILED
&& GetExitCodeProcess(process
, &exit_code
))
344 SETATTR_FileHandle_exit_status(interp
, filehandle
, exit_code
);
346 SETATTR_FileHandle_exit_status(interp
, filehandle
, 1);
347 CloseHandle(process
);
350 return (result
!= 0);
355 =item C<INTVAL Parrot_io_is_closed_win32(PARROT_INTERP, PMC *filehandle)>
357 Test whether the filehandle has been closed.
364 Parrot_io_is_closed_win32(PARROT_INTERP
, ARGIN(PMC
*filehandle
))
366 ASSERT_ARGS(Parrot_io_is_closed_win32
)
367 if (Parrot_io_get_os_handle(interp
, filehandle
) == INVALID_HANDLE_VALUE
)
375 =item C<static INTVAL io_is_tty_win32(PIOHANDLE fd)>
377 Returns whether C<fd> is a console/tty.
383 PARROT_WARN_UNUSED_RESULT
385 io_is_tty_win32(PIOHANDLE fd
)
387 ASSERT_ARGS(io_is_tty_win32
)
388 const DWORD ftype
= GetFileType(fd
);
389 return (ftype
== FILE_TYPE_CHAR
);
394 =item C<INTVAL Parrot_io_flush_win32(PARROT_INTERP, PMC *filehandle)>
396 Calls C<FlushFileBuffers()> to flush C<*io>'s file descriptor.
403 Parrot_io_flush_win32(PARROT_INTERP
, ARGMOD(PMC
*filehandle
))
405 ASSERT_ARGS(Parrot_io_flush_win32
)
407 * FlushFileBuffers won't work for console handles. From the MS help file:
409 * Windows NT: The function fails if hFile is a handle to console
410 * output. That is because console output is not buffered. The
411 * function returns FALSE, and GetLastError returns
412 * ERROR_INVALID_HANDLE.
414 * Windows 9x: The function does nothing if hFile is a handle to
415 * console output. That is because console output is not buffered.
416 * The function returns TRUE, but it does nothing.
418 return FlushFileBuffers(Parrot_io_get_os_handle(interp
, filehandle
));
423 =item C<size_t Parrot_io_read_win32(PARROT_INTERP, PMC *filehandle, STRING
426 Calls C<ReadFile()> to read up to C<len> bytes from C<*io>'s file
427 descriptor to the memory starting at C<buffer>.
434 Parrot_io_read_win32(PARROT_INTERP
,
435 ARGMOD(PMC
*filehandle
),
436 ARGOUT(STRING
**buf
))
438 ASSERT_ARGS(Parrot_io_read_win32
)
444 s
= Parrot_io_make_string(interp
, buf
, 2048);
446 buffer
= Buffer_bufstart(s
);
448 if (ReadFile(Parrot_io_get_os_handle(interp
, filehandle
),
449 (LPVOID
) buffer
, (DWORD
) len
, &countread
, NULL
)) {
451 s
->bufused
= s
->strlen
= countread
;
452 return (size_t)countread
;
455 /* EOF if read 0 and bytes were requested */
456 Parrot_io_set_flags(interp
, filehandle
,
457 (Parrot_io_get_flags(interp
, filehandle
) | PIO_F_EOF
));
460 /* FIXME : An error occured */
461 Parrot_io_set_flags(interp
, filehandle
,
462 (Parrot_io_get_flags(interp
, filehandle
) | PIO_F_EOF
));
465 s
->bufused
= s
->strlen
= 0;
471 =item C<size_t Parrot_io_write_win32(PARROT_INTERP, PMC *filehandle, const
474 Calls C<WriteFile()> to write C<len> bytes from the memory starting at
475 C<buffer> to C<*io>'s file descriptor. Returns C<(size_t)-1> on
483 Parrot_io_write_win32(PARROT_INTERP
, ARGIN(PMC
*filehandle
), ARGIN(const STRING
*s
))
485 ASSERT_ARGS(Parrot_io_write_win32
)
486 DWORD countwrote
= 0;
488 void * const buffer
= s
->strstart
;
489 DWORD len
= (DWORD
) s
->bufused
;
490 PIOHANDLE os_handle
= Parrot_io_get_os_handle(interp
, filehandle
);
492 /* do it by hand, Win32 hasn't any specific flag */
493 if (Parrot_io_get_flags(interp
, filehandle
) & PIO_F_APPEND
) {
497 p
.LowPart
= SetFilePointer(os_handle
, p
.LowPart
,
498 &p
.HighPart
, FILE_END
);
499 if (p
.LowPart
== 0xFFFFFFFF && (GetLastError() != NO_ERROR
)) {
500 /* Error - exception */
505 if (WriteFile(os_handle
, (LPCSTR
) buffer
, len
, &countwrote
, NULL
))
508 /* Write may have failed because of small buffers,
509 * see TT #710 for example.
510 * Let's try writing in small chunks */
511 if ((err
= GetLastError()) == ERROR_NOT_ENOUGH_MEMORY
|| err
== ERROR_INVALID_USER_BUFFER
) {
512 DWORD chunk
= 4096; /* Arbitrarily choosen value */
518 if (WriteFile(os_handle
, (LPCSTR
) buffer
, chunk
, &countwrote
, NULL
) == 0 ||
526 /* FIXME: Set error flag */
532 =item C<PIOOFF_T Parrot_io_seek_win32(PARROT_INTERP, PMC *filehandle, PIOOFF_T
537 Calls C<SetFilePointer()> to move the read/write position of C<*io>'s
538 file descriptor to C<off> bytes relative to the location specified by
546 Parrot_io_seek_win32(PARROT_INTERP
, ARGMOD(PMC
*filehandle
),
547 PIOOFF_T off
, INTVAL whence
)
549 ASSERT_ARGS(Parrot_io_seek_win32
)
550 LARGE_INTEGER offset
;
552 offset
.QuadPart
= off
;
553 /* offset.HighPart gets overwritten */
554 offset
.LowPart
= SetFilePointer(Parrot_io_get_os_handle(interp
, filehandle
),
555 offset
.LowPart
, &offset
.HighPart
, whence
);
556 if (offset
.LowPart
== 0xFFFFFFFF && (GetLastError() != NO_ERROR
)) {
557 /* Error - exception */
560 Parrot_io_set_file_position(interp
, filehandle
, offset
.QuadPart
);
561 return offset
.QuadPart
;
566 =item C<PIOOFF_T Parrot_io_tell_win32(PARROT_INTERP, PMC *filehandle)>
568 Returns the current read/write position of C<*io>'s file descriptor.
575 Parrot_io_tell_win32(PARROT_INTERP
, ARGIN(PMC
*filehandle
))
577 ASSERT_ARGS(Parrot_io_tell_win32
)
580 p
.QuadPart
= piooffsetzero
;
581 p
.LowPart
= SetFilePointer(Parrot_io_get_os_handle(interp
, filehandle
),
582 0, &p
.HighPart
, FILE_CURRENT
);
583 if (p
.LowPart
== 0xFFFFFFFF && GetLastError() != NO_ERROR
) {
584 /* FIXME: Error - exception */
591 =item C<size_t Parrot_io_peek_win32(PARROT_INTERP, PMC *filehandle, STRING
594 Retrieve the next character in the stream without modifying the stream. Not
595 implemented for this platform.
602 Parrot_io_peek_win32(PARROT_INTERP
,
603 SHIM(PMC
*filehandle
),
606 ASSERT_ARGS(Parrot_io_peek_win32
)
607 Parrot_ex_throw_from_c_args(interp
, NULL
, EXCEPTION_UNIMPLEMENTED
,
608 "peek() not implemented");
613 =item C<PMC * Parrot_io_open_pipe_win32(PARROT_INTERP, PMC *filehandle, STRING
614 *command, int flags)>
616 Open a pipe. Not implemented for this platform.
622 PARROT_WARN_UNUSED_RESULT
623 PARROT_CAN_RETURN_NULL
625 Parrot_io_open_pipe_win32(PARROT_INTERP
, ARGMOD(PMC
*filehandle
),
626 ARGIN(STRING
*command
), int flags
)
628 ASSERT_ARGS(Parrot_io_open_pipe_win32
)
630 HANDLE current
= GetCurrentProcess();
631 HANDLE hnull
= INVALID_HANDLE_VALUE
;
632 HANDLE hread
= INVALID_HANDLE_VALUE
;
633 HANDLE hwrite
= INVALID_HANDLE_VALUE
;
634 HANDLE hchild
= INVALID_HANDLE_VALUE
;
636 SECURITY_ATTRIBUTES sec
;
637 PROCESS_INFORMATION procinfo
;
640 PMC
*io
= PMC_IS_NULL(filehandle
) ? Parrot_io_new_pmc(interp
, flags
) : filehandle
;
642 int f_read
= (flags
& PIO_F_READ
) != 0;
643 int f_write
= (flags
& PIO_F_WRITE
) != 0;
644 if (f_read
== f_write
)
645 Parrot_ex_throw_from_c_args(interp
, NULL
, EXCEPTION_PIO_ERROR
,
646 "Invalid pipe mode: %X", flags
);
648 procinfo
.hThread
= INVALID_HANDLE_VALUE
;
649 procinfo
.hProcess
= INVALID_HANDLE_VALUE
;
650 sec
.nLength
= sizeof sec
;
651 sec
.lpSecurityDescriptor
= NULL
;
652 sec
.bInheritHandle
= TRUE
;
654 comspec
= getenv("COMSPEC");
657 auxcomm
= Parrot_str_new(interp
, comspec
, 0);
658 auxcomm
= Parrot_str_concat(interp
, auxcomm
, Parrot_str_new(interp
, " /c ", 0));
659 auxcomm
= Parrot_str_concat(interp
, auxcomm
, command
);
660 cmd
= Parrot_str_to_cstring(interp
, auxcomm
);
661 start
.cb
= sizeof start
;
662 GetStartupInfo(&start
);
663 start
.dwFlags
= STARTF_USESTDHANDLES
;
664 if (CreatePipe(&hread
, &hwrite
, NULL
, 0) == 0)
666 if (DuplicateHandle(current
, f_read
? hwrite
: hread
,
669 DUPLICATE_CLOSE_SOURCE
| DUPLICATE_SAME_ACCESS
)
672 if (hchild
== INVALID_HANDLE_VALUE
)
676 /* Redirect input to NULL. This is to avoid
677 * interferences in case both the child and
678 * the parent tries to read from stdin.
679 * May be unneccessary or even interfere
680 * with valid usages, need more feedback. */
681 hnull
= CreateFile("NUL", GENERIC_READ
|GENERIC_WRITE
,
682 0, &sec
, OPEN_EXISTING
,
683 FILE_ATTRIBUTE_NORMAL
, NULL
);
684 if (hnull
== INVALID_HANDLE_VALUE
)
686 start
.hStdInput
= hnull
;
687 start
.hStdOutput
= hchild
;
688 start
.hStdError
= hchild
;
691 start
.hStdInput
= hchild
;
694 if (CreateProcess(NULL
,
697 NULL
, NULL
, &start
, &procinfo
) == 0)
700 Parrot_io_set_os_handle(interp
, io
, hread
);
704 Parrot_io_set_os_handle(interp
, io
, hwrite
);
708 Parrot_str_free_cstring(cmd
);
709 CloseHandle(procinfo
.hThread
);
710 VTABLE_set_integer_keyed_int(interp
, io
, 0, (INTVAL
)procinfo
.hProcess
);
715 Parrot_str_free_cstring(cmd
);
716 if (hnull
!= INVALID_HANDLE_VALUE
)
718 if (hread
!= INVALID_HANDLE_VALUE
)
720 if (hwrite
!= INVALID_HANDLE_VALUE
)
722 if (hchild
!= INVALID_HANDLE_VALUE
)
724 if (procinfo
.hThread
!= INVALID_HANDLE_VALUE
)
725 CloseHandle(procinfo
.hThread
);
726 if (procinfo
.hProcess
!= INVALID_HANDLE_VALUE
)
727 CloseHandle(procinfo
.hProcess
);
728 Parrot_ex_throw_from_c_args(interp
, NULL
, EXCEPTION_UNIMPLEMENTED
,
734 =item C<INTVAL Parrot_io_pipe_win32(PARROT_INTERP, PIOHANDLE *reader, PIOHANDLE
737 Uses CreatePipe() to create a matched pair of pipe handles. Returns 0 on
738 success, -1 on failure.
744 PARROT_WARN_UNUSED_RESULT
745 PARROT_CAN_RETURN_NULL
747 Parrot_io_pipe_win32(SHIM_INTERP
, ARGMOD(PIOHANDLE
*reader
), ARGMOD(PIOHANDLE
*writer
))
749 ASSERT_ARGS(Parrot_io_pipe_win32
)
750 return CreatePipe(reader
, writer
, NULL
, 0) ? 0 : -1;
753 #endif /* PIO_OS_WIN32 */
764 F<src/io/io_private.h>.
765 F<include/parrot/io_win32.h>.
774 * c-file-style: "parrot"
776 * vim: expandtab shiftwidth=4: