1 /********************************************************************/
2 /*                                                                  */
3 /*  tim_win.c     Time functions which call the Windows API.        */
4 /*  Copyright (C) 1989 - 2011  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/tim_win.c                                       */
27 /*  Changes: 2006, 2009  Thomas Mertes                              */
28 /*  Content: Time 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 "stdio.h"
38 #include "string.h"
39 #include "time.h"
40 #include "windows.h"
41 #include "sys/stat.h"
42 #include "sys/types.h"
43 #ifdef INCLUDE_SYS_UTIME
44 #include "sys/utime.h"
45 #else
46 #include "utime.h"
47 #endif
48 #include "errno.h"
49 
50 #include "common.h"
51 #include "os_decls.h"
52 #include "striutl.h"
53 #include "tim_rtl.h"
54 #include "fil_rtl.h"
55 #include "stat_drv.h"
56 #include "rtl_err.h"
57 
58 #undef EXTERN
59 #define EXTERN
60 #include "tim_drv.h"
61 
62 
63 /* Seconds between 1601-01-01 and 1970-01-01 */
64 #define SECONDS_FROM_1601_TO_1970 INT64_SUFFIX(11644473600)
65 #define WINDOWS_TICK UINT64_SUFFIX(10000000)
66 
67 #define USE_UTIME_ORIG 0
68 
69 
70 
71 /**
72  *  Wait until the given time is reached
73  *  @param time_zone Difference to UTC in minutes (for UTC+1 it is 60).
74  *                   The time_zone includes the effect of a daylight saving time.
75  */
timAwait(intType year,intType month,intType day,intType hour,intType min,intType sec,intType micro_sec,intType time_zone)76 void timAwait (intType year, intType month, intType day, intType hour,
77     intType min, intType sec, intType micro_sec, intType time_zone)
78 
79   {
80     SYSTEMTIME await_time_struct;
81     union {
82       uint64Type nanosecs100; /*time since 1 Jan 1601 in 100ns units */
83       FILETIME filetime;
84     } await_file_time, current_time;
85     uint64Type await_second;
86     uint64Type current_second;
87     intType current_micro_sec;
88     unsigned long wait_milliseconds;
89 
90   /* timAwait */
91     logFunction(printf("timAwait(" F_D(04) "-" F_D(02) "-" F_D(02) " "
92                                    F_D(02) ":" F_D(02) ":" F_D(02) "."
93                                    F_D(06) ", " FMT_D ")\n",
94                        year, month, day, hour, min, sec, micro_sec, time_zone););
95     await_time_struct.wYear         = (WORD) year;
96     await_time_struct.wMonth        = (WORD) month;
97     await_time_struct.wDay          = (WORD) day;
98     await_time_struct.wHour         = (WORD) hour;
99     await_time_struct.wMinute       = (WORD) min;
100     await_time_struct.wSecond       = (WORD) sec;
101     await_time_struct.wMilliseconds = 0;
102     if (unlikely(SystemTimeToFileTime(
103         &await_time_struct, &await_file_time.filetime) == 0)) {
104       logError(printf("timAwait: SystemTimeToFileTime() failed.\n"););
105       raise_error(RANGE_ERROR);
106     } else {
107       await_second = await_file_time.nanosecs100 / 10000000;
108       await_second = (uint64Type) ((int64Type) await_second - time_zone * 60);
109 
110       GetSystemTimeAsFileTime(&current_time.filetime);
111       current_second = current_time.nanosecs100 / 10000000;
112       current_micro_sec = (intType) ((current_time.nanosecs100 / 10) % 1000000);
113       if (current_second < await_second ||
114           (current_second == await_second &&
115           current_micro_sec < micro_sec)) {
116         wait_milliseconds = (unsigned long) (await_second - current_second) * 1000;
117         if (micro_sec >= current_micro_sec) {
118           wait_milliseconds += (unsigned long) ((micro_sec - current_micro_sec) / 1000);
119         } else {
120           wait_milliseconds -= (unsigned long) ((current_micro_sec - micro_sec) / 1000);
121         } /* if */
122         Sleep(wait_milliseconds);
123       } /* if */
124     } /* if */
125     logFunction(printf("timAwait -->\n"););
126   } /* timAwait */
127 
128 
129 
130 /**
131  *  Return the current time in microseconds since the epoch of the system.
132  *  This function is used to initialize the random number generator and
133  *  for the simple profiling.
134  */
timMicroSec(void)135 intType timMicroSec (void)
136 
137   {
138     union {
139       uint64Type nanosecs100; /*time since 1 Jan 1601 in 100ns units */
140       FILETIME filetime;
141     } utc_time;
142     intType micro_sec;
143 
144   /* timMicroSec */
145     GetSystemTimeAsFileTime(&utc_time.filetime);
146     micro_sec = (intType) (utc_time.nanosecs100 / 10);
147     logFunction(printf("timMicroSec() --> " FMT_U "\n", micro_sec););
148     return micro_sec;
149   } /* timMicroSec */
150 
151 
152 
153 /**
154  *  Determine the current local time.
155  *  @param time_zone Difference to UTC in minutes (for UTC+1 it is 60).
156  *                   The time_zone includes the effect of a daylight saving time.
157  *  @param is_dst Is TRUE, if a daylight saving time is currently in effect.
158  */
timNow(intType * year,intType * month,intType * day,intType * hour,intType * min,intType * sec,intType * micro_sec,intType * time_zone,boolType * is_dst)159 void timNow (intType *year, intType *month, intType *day, intType *hour,
160     intType *min, intType *sec, intType *micro_sec, intType *time_zone,
161     boolType *is_dst)
162 
163   {
164     union {
165       uint64Type nanosecs100; /*time since 1 Jan 1601 in 100ns units */
166       FILETIME filetime;
167     } utc_time;
168     time_t utc_seconds;
169 #if defined USE_LOCALTIME_R || defined USE_LOCALTIME_S
170     struct tm tm_result;
171 #endif
172     struct tm *local_time;
173 
174   /* timNow */
175     logFunction(printf("timNow\n"););
176     GetSystemTimeAsFileTime(&utc_time.filetime);
177     utc_seconds = (time_t) ((int64Type) (utc_time.nanosecs100 / WINDOWS_TICK) -
178         SECONDS_FROM_1601_TO_1970);
179 #if defined USE_LOCALTIME_R
180     local_time = localtime_r(&utc_seconds, &tm_result);
181 #elif defined USE_LOCALTIME_S
182     if (localtime_s(&tm_result, &utc_seconds) != 0) {
183       local_time = NULL;
184     } else {
185       local_time = &tm_result;
186     } /* if */
187 #else
188     local_time = localtime(&utc_seconds);
189 #endif
190     if (unlikely(local_time == NULL)) {
191       logError(printf("timNow: One of "
192                       "localtime/localtime_r/localtime_s(" FMT_T ") failed:\n"
193                       "errno=%d\nerror: %s\n",
194                       utc_seconds, errno, strerror(errno)););
195       raise_error(RANGE_ERROR);
196     } else {
197       *year      = local_time->tm_year + 1900;
198       *month     = local_time->tm_mon + 1;
199       *day       = local_time->tm_mday;
200       *hour      = local_time->tm_hour;
201       *min       = local_time->tm_min;
202       *sec       = local_time->tm_sec;
203       *micro_sec = (intType) ((utc_time.nanosecs100 / 10) % 1000000);
204       *time_zone = (unchecked_mkutc(local_time) - utc_seconds) / 60;
205       *is_dst    = local_time->tm_isdst > 0;
206     } /* if */
207     logFunction(printf("timNow(" F_D(04) "-" F_D(02) "-" F_D(02) " "
208                                  F_D(02) ":" F_D(02) ":" F_D(02) "."
209                                  F_D(06) ", " FMT_D ", %d) -->\n",
210                        *year, *month, *day, *hour, *min, *sec,
211                        *micro_sec, *time_zone, *is_dst););
212   } /* timNow */
213 
214 
215 
216 #ifdef USE_ALTERNATE_LOCALTIME_R
217 #undef localtime_r
218 
219 
220 
alternate_localtime_r(time_t * utc_seconds,struct tm * tm_result)221 struct tm *alternate_localtime_r (time_t *utc_seconds, struct tm *tm_result)
222 
223   {
224     union {
225       uint64Type nanosecs100; /*time since 1 Jan 1601 in 100ns units */
226       FILETIME filetime;
227     } utc_time, time_zone_delta;
228     SYSTEMTIME utc_time_struct;
229     SYSTEMTIME local_time_struct;
230     static time_t time_zone_seconds = 1;
231 
232   /* alternate_localtime_r */
233     logFunction(printf("alternate_localtime_r(%ld)\n", *utc_seconds););
234     if (time_zone_seconds == 1) {
235       utc_time.nanosecs100 = (uint64Type) SECONDS_FROM_1601_TO_1970 * WINDOWS_TICK;
236       /* FileTimeToLocalFileTime(&utc_time.filetime, &time_zone_delta.filetime);
237       time_zone_seconds = time_zone_delta.nanosecs100 / WINDOWS_TICK - SECONDS_FROM_1601_TO_1970; */
238       if (unlikely(FileTimeToSystemTime(&utc_time.filetime, &utc_time_struct) == 0)) {
239         return NULL;
240       } else if (unlikely(SystemTimeToTzSpecificLocalTime(
241           NULL, &utc_time_struct, &local_time_struct) == 0)) {
242         return NULL;
243       } else {
244         tm_result->tm_year  = local_time_struct.wYear - 1900;
245         tm_result->tm_mon   = local_time_struct.wMonth - 1;
246         tm_result->tm_mday  = local_time_struct.wDay;
247         tm_result->tm_hour  = local_time_struct.wHour;
248         tm_result->tm_min   = local_time_struct.wMinute;
249         tm_result->tm_sec   = local_time_struct.wSecond;
250         time_zone_seconds = unchecked_mkutc(tm_result);
251         /* printf("%ld\n", time_zone_seconds); */
252       } /* if */
253     } /* if */
254     utc_time.nanosecs100 = (uint64Type) (
255         (int64Type) *utc_seconds + SECONDS_FROM_1601_TO_1970) * WINDOWS_TICK;
256     if (unlikely(FileTimeToSystemTime(&utc_time.filetime, &utc_time_struct) == 0)) {
257       return NULL;
258     } else if (unlikely(SystemTimeToTzSpecificLocalTime(
259         NULL, &utc_time_struct, &local_time_struct) == 0)) {
260       return NULL;
261     } else {
262       tm_result->tm_year  = local_time_struct.wYear - 1900;
263       tm_result->tm_mon   = local_time_struct.wMonth - 1;
264       tm_result->tm_mday  = local_time_struct.wDay;
265       tm_result->tm_hour  = local_time_struct.wHour;
266       tm_result->tm_min   = local_time_struct.wMinute;
267       tm_result->tm_sec   = local_time_struct.wSecond;
268       tm_result->tm_isdst = unchecked_mkutc(tm_result) - *utc_seconds != time_zone_seconds;
269     } /* if */
270     return tm_result;
271   } /* alternate_localtime_r */
272 #endif
273 
274 
275 
276 #ifdef USE_ALTERNATE_UTIME
277 /**
278  *  Change the access and modification times of a file.
279  *  Since a directory is a form of file the utime function should
280  *  work for directories also. Unfortunately the windows function
281  *  utime does not behave this way. Under windows utime fails with
282  *  EACCES if it is called for a directory. In this case
283  *  alternate_utime uses win32 functions to work for directories.
284  *  If the windows function utime is called for a volume it
285  *  fails with ENOENT. This case is recognized and alternate_utime
286  *  uses win32 functions to work for volumes.
287  */
alternate_utime(const wchar_t * os_path,os_utimbuf_struct * utime_buf)288 int alternate_utime (const wchar_t *os_path, os_utimbuf_struct *utime_buf)
289 
290   {
291 #if USE_UTIME_ORIG && defined os_utime_orig
292     os_stat_struct stat_buf;
293 #endif
294     HANDLE filehandle;
295     union {
296       uint64Type nanosecs100; /*time since 1 Jan 1601 in 100ns units */
297       FILETIME filetime;
298     } actime, modtime;
299     int result;
300 
301   /* alternate_utime */
302     logFunction(printf("alternate_utime(\"%ls\", "
303                        "{actime=" FMT_T ", modtime=" FMT_T "})\n",
304                        os_path, utime_buf->actime, utime_buf->modtime););
305 #if USE_UTIME_ORIG && defined os_utime_orig
306     result = os_utime_orig(os_path, utime_buf);
307     logErrorIfTrue(result != 0,
308                    printf("alternate_utime: os_utime_orig(\"%ls\", ...) failed:\n"
309                           "errno=%d\nerror: %s\n",
310                           os_path, errno, strerror(errno)););
311 #ifdef UTIME_ORIG_BUGGY_FOR_FAT_FILES
312     /* A FAT filesystem has a mtime resolution of 2 seconds. */
313     /* The error causes an even mtime to be set 2 seconds too big. */
314     if (result == 0 && (utime_buf->modtime & 1) == 0 &&
315         os_stat(os_path, &stat_buf) == 0) {
316       /* printf("new atime=%ld\n", stat_buf.st_atime);
317          printf("new mtime=%ld\n", stat_buf.st_mtime); */
318       if (stat_buf.st_mtime - utime_buf->modtime == 2) {
319         utime_buf->modtime -= 2;
320         result = os_utime_orig(os_path, utime_buf);
321         utime_buf->modtime += 2;
322       } /* if */
323     } /* if */
324 #endif
325     if (result != 0 &&
326         ((errno == EACCES &&
327           os_stat(os_path, &stat_buf) == 0 && S_ISDIR(stat_buf.st_mode)) ||
328         (errno == ENOENT &&
329           ((os_path[PREFIX_LEN] >= 'a' && os_path[PREFIX_LEN] <= 'z') ||
330            (os_path[PREFIX_LEN] >= 'A' && os_path[PREFIX_LEN] <= 'Z')) &&
331           os_path[PREFIX_LEN + 1] == ':' && os_path[PREFIX_LEN + 2] == '\\' &&
332           os_path[PREFIX_LEN + 3] == '\0'))) {
333       /* printf("old atime=" FMT_T "\n", stat_buf.st_atime);
334          printf("old mtime=" FMT_T "\n", stat_buf.st_mtime); */
335 #endif
336       filehandle = CreateFileW(os_path, FILE_WRITE_ATTRIBUTES,
337                                FILE_SHARE_READ | FILE_SHARE_DELETE, NULL,
338                                OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
339       if (unlikely(filehandle == INVALID_HANDLE_VALUE)) {
340         logError(printf("alternate_utime: CreateFileW(\"%ls\", ...) failed:\n"
341                         "GetLastError=" FMT_U32 "\n",
342                         os_path, (uint32Type) GetLastError()););
343         errno = EACCES;
344         result = -1;
345       } else {
346         /* The case of utime_buf == NULL is not considered,   */
347         /* since alternate_utime will never be used this way. */
348 #if TIME_T_SIGNED
349         actime.nanosecs100 = (uint64Type) (
350             (int64Type) utime_buf->actime + SECONDS_FROM_1601_TO_1970) * WINDOWS_TICK;
351         modtime.nanosecs100 = (uint64Type) (
352             (int64Type) utime_buf->modtime + SECONDS_FROM_1601_TO_1970) * WINDOWS_TICK;
353 #else
354         actime.nanosecs100 = ((uint64Type) utime_buf->actime +
355             (uint64Type) SECONDS_FROM_1601_TO_1970) * WINDOWS_TICK;
356         modtime.nanosecs100 = ((uint64Type) utime_buf->modtime +
357             (uint64Type) SECONDS_FROM_1601_TO_1970) * WINDOWS_TICK;
358 #endif
359         /* printf("actime=" FMT_T " " FMT_U64 "\n",
360                   utime_buf->actime, actime.nanosecs100);
361            printf("modtime=" FMT_T " " FMT_U64 "\n",
362                   utime_buf->modtime, modtime.nanosecs100); */
363         if (unlikely(SetFileTime(filehandle, NULL, &actime.filetime,
364                                  &modtime.filetime) == 0)) {
365           logError(printf("alternate_utime(\"%ls\", ...): SetFileTime() failed:\n"
366                           "GetLastError=" FMT_U32 "\n",
367                           os_path, (uint32Type) GetLastError()););
368           errno = EACCES;
369           result = -1;
370         } else {
371           result = 0;
372         } /* if */
373         CloseHandle(filehandle);
374       } /* if */
375 #if USE_UTIME_ORIG && defined os_utime_orig
376     } /* if */
377 #endif
378     logFunction(printf("alternate_utime --> %d\n", result););
379     return result;
380   } /* alternate_utime */
381 #endif
382