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