1 /********************************************************************/
2 /* */
3 /* fil_win.c File functions which call the Windows API. */
4 /* Copyright (C) 1989 - 2012 Thomas Mertes */
5 /* */
6 /* This file is part of the Seed7 Runtime Library. */
7 /* */
8 /* The Seed7 Runtime Library is free software; you can */
9 /* redistribute it and/or modify it under the terms of the GNU */
10 /* Lesser General Public License as published by the Free Software */
11 /* Foundation; either version 2.1 of the License, or (at your */
12 /* option) any later version. */
13 /* */
14 /* The Seed7 Runtime Library is distributed in the hope that it */
15 /* will be useful, but WITHOUT ANY WARRANTY; without even the */
16 /* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR */
17 /* PURPOSE. See the GNU Lesser General Public License for more */
18 /* details. */
19 /* */
20 /* You should have received a copy of the GNU Lesser General */
21 /* Public License along with this program; if not, write to the */
22 /* Free Software Foundation, Inc., 51 Franklin Street, */
23 /* Fifth Floor, Boston, MA 02110-1301, USA. */
24 /* */
25 /* Module: Seed7 Runtime Library */
26 /* File: seed7/src/fil_win.c */
27 /* Changes: 2011, 2012 Thomas Mertes */
28 /* Content: File functions which call the Windows API. */
29 /* */
30 /********************************************************************/
31
32 #define LOG_FUNCTIONS 0
33 #define VERBOSE_EXCEPTIONS 0
34
35 #include "version.h"
36
37 #include "stdlib.h"
38 #include "stdio.h"
39 #include "windows.h"
40 #include "io.h"
41 #include "fcntl.h"
42 #include "sys/types.h"
43 #include "sys/stat.h"
44 #include "signal.h"
45 #if STDINT_H_PRESENT
46 #include "stdint.h"
47 #endif
48 #if UNISTD_H_PRESENT
49 #include "unistd.h"
50 #endif
51 #include "errno.h"
52
53 #include "common.h"
54 #include "os_decls.h"
55 #include "heaputl.h"
56 #include "stat_drv.h"
57 #include "fil_rtl.h"
58 #include "rtl_err.h"
59
60
61 #if !INTPTR_T_DEFINED
62 #if POINTER_SIZE == 32
63 typedef int32Type intptr_t;
64 #elif POINTER_SIZE == 64
65 typedef int64Type intptr_t;
66 #endif
67 #endif
68
69 #if HAS_SIGACTION || HAS_SIGNAL
70 static volatile int waitForHandler;
71 #endif
72
73
74
75 #if HAS_SIGACTION || HAS_SIGNAL
handleIntSignal(int sig_num)76 static void handleIntSignal (int sig_num)
77
78 {
79 #if SIGNAL_RESETS_HANDLER
80 signal(SIGINT, handleIntSignal);
81 #endif
82 waitForHandler = 0;
83 }
84
85
86
87 /**
88 * Read a character from 'inFile' and check if ctrl-c is pressed.
89 * @param inFile File from which the character is read.
90 * @param sigintReceived Flag indicating if ctrl-c has been pressed.
91 * @return the character read.
92 */
readCharChkCtrlC(cFileType inFile,boolType * sigintReceived)93 int readCharChkCtrlC (cFileType inFile, boolType *sigintReceived)
94
95 {
96 #if HAS_SIGACTION
97 struct sigaction sigAct;
98 struct sigaction oldSigAct;
99 #elif HAS_SIGNAL
100 void (*oldSigHandler) (int);
101 #endif
102 int ch = ' ';
103
104 /* readCharChkCtrlC */
105 logFunction(printf("readCharChkCtrlC(%d, %d)\n",
106 safe_fileno(inFile), *sigintReceived););
107 #if HAS_SIGACTION
108 sigAct.sa_handler = handleIntSignal;
109 sigemptyset(&sigAct.sa_mask);
110 sigAct.sa_flags = SA_RESTART;
111 if (unlikely(sigaction(SIGINT, &sigAct, &oldSigAct) != 0)) {
112 #elif HAS_SIGNAL
113 oldSigHandler = signal(SIGINT, handleIntSignal);
114 if (unlikely(oldSigHandler == SIG_ERR)) {
115 #endif
116 logError(printf("readCharChkCtrlC(%d, *): "
117 "signal(SIGINT, handleIntSignal) failed:\n"
118 "errno=%d\nerror: %s\n",
119 safe_fileno(inFile), errno, strerror(errno)););
120 raise_error(FILE_ERROR);
121 } else {
122 waitForHandler = 1;
123 ch = getc(inFile);
124 *sigintReceived = feof(inFile);
125 if (*sigintReceived) {
126 clearerr(inFile);
127 fflush(inFile);
128 /* The interrupt thread runs in parallel. */
129 /* Wait until handleIntSignal() has finished. */
130 while (waitForHandler) ;
131 } /* if */
132 #if HAS_SIGACTION
133 if (unlikely(sigaction(SIGINT, &oldSigAct, NULL) != 0)) {
134 #elif HAS_SIGNAL
135 if (unlikely(signal(SIGINT, oldSigHandler) == SIG_ERR)) {
136 #endif
137 logError(printf("readCharChkCtrlC(%d, *): "
138 "signal(SIGINT, oldSigHandler) failed:\n"
139 "errno=%d\nerror: %s\n",
140 safe_fileno(inFile), errno, strerror(errno)););
141 raise_error(FILE_ERROR);
142 } /* if */
143 } /* if */
144 logFunction(printf("readCharChkCtrlC(%d, %d) --> %d\n",
145 safe_fileno(inFile), *sigintReceived, ch););
146 return ch;
147 } /* readCharChkCtrlC */
148 #endif
149
150
151
152 static boolType stdinReady (void)
153
154 {
155 HANDLE hKeyboard;
156 DWORD numEvents;
157 INPUT_RECORD *events;
158 DWORD count;
159 DWORD idx;
160 boolType result = FALSE;
161
162 /* stdinReady */
163 logFunction(printf("stdinReady\n"););
164 hKeyboard = GetStdHandle(STD_INPUT_HANDLE);
165 if (hKeyboard != INVALID_HANDLE_VALUE &&
166 GetNumberOfConsoleInputEvents(hKeyboard, &numEvents) != 0) {
167 /* printf("numEvents: %lu\n", (unsigned long) numEvents); */
168 events = malloc(sizeof(INPUT_RECORD) * numEvents);
169 if (events != NULL) {
170 if (PeekConsoleInputW(hKeyboard, events, numEvents, &count) != 0) {
171 for (idx = 0; idx < count; idx++) {
172 /* printf("EventType: %d\n", events[idx].EventType); */
173 if (events[idx].EventType == KEY_EVENT &&
174 events[idx].Event.KeyEvent.bKeyDown &&
175 events[idx].Event.KeyEvent.wVirtualKeyCode == VK_RETURN) {
176 result = TRUE;
177 } /* if */
178 } /* for */
179 } /* if */
180 free(events);
181 } /* if */
182 } /* if */
183 logFunction(printf("stdinReady --> %d\n", result););
184 return result;
185 } /* stdinReady */
186
187
188
189 /**
190 * Determine if at least one character can be read without blocking.
191 * Blocking means that 'getc' would wait until a character is
192 * received. Blocking can last for a period of unspecified length.
193 * Regular files do not block.
194 * @return TRUE if 'getc' would not block, FALSE otherwise.
195 */
196 boolType filInputReady (fileType inFile)
197
198 {
199 cFileType cInFile;
200 int file_no;
201 HANDLE fileHandle;
202 os_fstat_struct stat_buf;
203 DWORD totalBytesAvail;
204 boolType inputReady;
205
206 /* filInputReady */
207 logFunction(printf("filInputReady(%s%d)\n",
208 inFile == NULL ? "NULL " : "",
209 inFile != NULL ? safe_fileno(inFile->cFile) : 0););
210 cInFile = inFile->cFile;
211 file_no = fileno(cInFile);
212 if (unlikely(file_no == -1)) {
213 logError(printf("filInputReady(%d): fileno(%d) failed:\n"
214 "errno=%d\nerror: %s\n",
215 safe_fileno(cInFile), safe_fileno(cInFile),
216 errno, strerror(errno)););
217 raise_error(FILE_ERROR);
218 inputReady = FALSE;
219 } else if (unlikely(os_fstat(file_no, &stat_buf) != 0)) {
220 logError(printf("filInputReady(%d): fstat(%d, *) failed:\n"
221 "errno=%d\nerror: %s\n",
222 safe_fileno(cInFile), safe_fileno(cInFile),
223 errno, strerror(errno)););
224 raise_error(FILE_ERROR);
225 inputReady = FALSE;
226 } else {
227 if (S_ISREG(stat_buf.st_mode)) {
228 inputReady = TRUE;
229 } else if (S_ISCHR(stat_buf.st_mode)) {
230 /* printf("read_buffer_empty(cInFile)=%d\n", read_buffer_empty(cInFile)); */
231 if (!read_buffer_empty(cInFile)) {
232 inputReady = TRUE;
233 } else if (file_no == 0) {
234 inputReady = stdinReady();
235 /* printf("stdinReady()=%d\n", result); */
236 } else {
237 fileHandle = (HANDLE) _get_osfhandle(file_no);
238 if (unlikely(fileHandle == (HANDLE) -1)) {
239 logError(printf("filInputReady(%d): _get_osfhandle(%d) failed:\n"
240 "errno=%d\nerror: %s\n",
241 safe_fileno(cInFile), safe_fileno(cInFile),
242 errno, strerror(errno)););
243 raise_error(FILE_ERROR);
244 inputReady = FALSE;
245 } else {
246 inputReady = WaitForSingleObject(fileHandle, 0) == WAIT_OBJECT_0;
247 } /* if */
248 } /* if */
249 } else if (S_ISFIFO(stat_buf.st_mode)) {
250 /* printf("read_buffer_empty(cInFile)=%d\n", read_buffer_empty(cInFile)); */
251 if (!read_buffer_empty(cInFile)) {
252 inputReady = TRUE;
253 } else {
254 fileHandle = (HANDLE) _get_osfhandle(file_no);
255 if (unlikely(fileHandle == (HANDLE) -1)) {
256 logError(printf("filInputReady(%d): _get_osfhandle(%d) failed:\n"
257 "errno=%d\nerror: %s\n",
258 safe_fileno(cInFile), safe_fileno(cInFile),
259 errno, strerror(errno)););
260 raise_error(FILE_ERROR);
261 inputReady = FALSE;
262 } else {
263 if (PeekNamedPipe(fileHandle, NULL, 0, NULL, &totalBytesAvail, NULL) != 0) {
264 inputReady = totalBytesAvail >= 1;
265 } else if (GetLastError() == ERROR_BROKEN_PIPE || feof(cInFile)) {
266 inputReady = TRUE;
267 } else {
268 logError(printf("filInputReady(%d): PeekNamedPipe(" FMT_U_MEM ", ...) failed:\n"
269 "errno=%d\nerror: %s\n",
270 safe_fileno(cInFile), (memSizeType) fileHandle,
271 errno, strerror(errno)););
272 raise_error(FILE_ERROR);
273 inputReady = FALSE;
274 } /* if */
275 } /* if */
276 } /* if */
277 } else {
278 logError(printf("filInputReady(%d): Unknown file type %d.\n",
279 safe_fileno(cInFile), stat_buf.st_mode & S_IFMT););
280 raise_error(FILE_ERROR);
281 inputReady = FALSE;
282 } /* if */
283 } /* if */
284 logFunction(printf("filInputReady(%d) --> %d\n",
285 safe_fileno(cInFile), inputReady););
286 return inputReady;
287 } /* filInputReady */
288
289
290
291 void filPipe (fileType *inFile, fileType *outFile)
292
293 {
294 fileType pipeInFile = NULL;
295 fileType pipeOutFile = NULL;
296 SECURITY_ATTRIBUTES saAttr;
297 HANDLE pipeReadHandle = INVALID_HANDLE_VALUE;
298 HANDLE pipeWriteHandle = INVALID_HANDLE_VALUE;
299 int pipeReadFildes;
300 int pipeWriteFildes;
301 cFileType cPipeInFile;
302 cFileType cPipeOutFile;
303 errInfoType err_info = OKAY_NO_ERROR;
304
305 /* filPipe */
306 if (unlikely(!ALLOC_RECORD(pipeInFile, fileRecord, count.files) ||
307 !ALLOC_RECORD(pipeOutFile, fileRecord, count.files))) {
308 err_info = MEMORY_ERROR;
309 } else {
310 saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
311 saAttr.bInheritHandle = TRUE;
312 saAttr.lpSecurityDescriptor = NULL;
313 if (unlikely(CreatePipe(&pipeReadHandle,
314 &pipeWriteHandle, &saAttr, 0) == 0)) {
315 logError(printf("filPipe: CreatePipe() failed.\n"););
316 err_info = FILE_ERROR;
317 } else {
318 if (unlikely((pipeReadFildes =
319 _open_osfhandle((intptr_t) (pipeReadHandle), _O_TEXT)) == -1)) {
320 logError(printf("filPipe: _open_osfhandle() failed:\n"););
321 CloseHandle(pipeReadHandle);
322 CloseHandle(pipeWriteHandle);
323 err_info = FILE_ERROR;
324 } else if (unlikely((pipeWriteFildes =
325 _open_osfhandle((intptr_t) (pipeWriteHandle), _O_TEXT)) == -1)) {
326 logError(printf("filPipe: _open_osfhandle() failed:\n"););
327 _close(pipeReadFildes);
328 CloseHandle(pipeWriteHandle);
329 err_info = FILE_ERROR;
330 } else if (unlikely((cPipeInFile = fdopen(pipeReadFildes, "rb")) == NULL)) {
331 logError(printf("filPipe: fdopen(%d, \"rb\") failed:\n"
332 "errno=%d\nerror: %s\n",
333 pipeReadFildes, errno, strerror(errno)););
334 _close(pipeReadFildes);
335 _close(pipeWriteFildes);
336 err_info = FILE_ERROR;
337 } else if (unlikely((cPipeOutFile = fdopen(pipeWriteFildes, "wb")) == NULL)) {
338 logError(printf("filPipe: fdopen(%d, \"wb\") failed:\n"
339 "errno=%d\nerror: %s\n",
340 pipeWriteFildes, errno, strerror(errno)););
341 fclose(cPipeInFile);
342 _close(pipeWriteFildes);
343 err_info = FILE_ERROR;
344 } else {
345 filDestr(*inFile);
346 initFileType(pipeInFile, 1);
347 pipeInFile->cFile = cPipeInFile;
348 *inFile = pipeInFile;
349 filDestr(*outFile);
350 initFileType(pipeOutFile, 1);
351 pipeOutFile->cFile = cPipeOutFile;
352 *outFile = pipeOutFile;
353 } /* if */
354 } /* if */
355 } /* if */
356 if (unlikely(err_info != OKAY_NO_ERROR)) {
357 if (pipeInFile != NULL) {
358 FREE_RECORD(pipeInFile, fileRecord, count.files);
359 } /* if */
360 if (pipeOutFile != NULL) {
361 FREE_RECORD(pipeOutFile, fileRecord, count.files);
362 } /* if */
363 } /* if */
364 logFunction(printf("filPipe --> {%s%d, %s%d}\n",
365 *inFile == NULL ? "NULL " : "",
366 *inFile != NULL ? safe_fileno((*inFile)->cFile) : 0,
367 *outFile == NULL ? "NULL " : "",
368 *outFile != NULL ? safe_fileno((*outFile)->cFile) : 0));
369 } /* filPipe */
370
371
372
373 #ifdef DEFINE_FTELLI64_EXT
374 #if DEFINE_FTELLI64_EXT == 1
375 os_off_t ftelli64Ext (FILE *aFile)
376
377 {
378 fpos_t pos;
379 os_off_t filePosition;
380
381 /* ftelli64Ext */
382 if (fgetpos(aFile, &pos) == 0) {
383 memcpy(&filePosition, &pos, sizeof(os_off_t));
384 } else {
385 filePosition = -1;
386 } /* if */
387 return filePosition;
388 } /* ftelli64Ext */
389
390
391
392 #elif DEFINE_FTELLI64_EXT == 2
393 os_off_t ftelli64Ext (FILE *aFile)
394
395 {
396 int file_no;
397 fpos_t pos;
398 os_off_t filePosition;
399
400 /* ftelli64Ext */
401 file_no = fileno(aFile);
402 if (file_no == -1) {
403 filePosition = -1;
404 } else {
405 /* Use fgetpos() and fsetpos() to ensure, that the internal buffer */
406 /* of aFile is synchronized with the underlying file descriptor file. */
407 /* This way _telli64() returns the same value as _ftelli64() would do. */
408 if (fgetpos(aFile, &pos) == 0 && fsetpos(aFile, &pos) == 0) {
409 filePosition = _telli64(file_no);
410 } else {
411 filePosition = -1;
412 } /* if */
413 } /* if */
414 return filePosition;
415 } /* ftelli64Ext */
416
417 #endif
418 #endif
419
420
421
422 #if !HAS_SNPRINTF
423 int snprintf (char *buffer, size_t bufsize, const char *fmt, ...)
424
425 {
426 va_list ap;
427 int result;
428
429 /* snprintf */
430 if (bufsize == 0) {
431 result = 0;
432 } else {
433 va_start(ap, fmt);
434 result = _vsnprintf(buffer, bufsize - 1, fmt, ap);
435 if (result < 0) {
436 buffer[bufsize - 1] = '\0';
437 result = (int) bufsize;
438 } else if ((unsigned int) result == bufsize - 1) {
439 buffer[bufsize - 1] = '\0';
440 } /* if */
441 va_end(ap);
442 } /* if */
443 return result;
444 } /* snprintf */
445 #endif
446
447
448
449 void setupFiles (void)
450
451 {
452 HANDLE hConsole;
453 DWORD mode;
454
455 /* setupFiles */
456 stdinFileRecord.cFile = stdin;
457 stdoutFileRecord.cFile = stdout;
458 stderrFileRecord.cFile = stderr;
459 /* Redirected files are set to _O_BINARY mode. */
460 /* Only real console files are left in _O_TEXT mode. */
461 /* This way the ENTER key is translated to '\n'. */
462 hConsole = GetStdHandle(STD_INPUT_HANDLE);
463 if (hConsole == INVALID_HANDLE_VALUE ||
464 GetFileType(hConsole) != FILE_TYPE_CHAR ||
465 GetConsoleMode(hConsole, &mode) == 0) {
466 setmode(fileno(stdin), _O_BINARY);
467 } /* if */
468 hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
469 if (hConsole == INVALID_HANDLE_VALUE ||
470 GetFileType(hConsole) != FILE_TYPE_CHAR ||
471 GetConsoleMode(hConsole, &mode) == 0) {
472 setmode(fileno(stdout), _O_BINARY);
473 } /* if */
474 hConsole = GetStdHandle(STD_ERROR_HANDLE);
475 if (hConsole == INVALID_HANDLE_VALUE ||
476 GetFileType(hConsole) != FILE_TYPE_CHAR ||
477 GetConsoleMode(hConsole, &mode) == 0) {
478 setmode(fileno(stderr), _O_BINARY);
479 } /* if */
480 } /* setupFiles */
481