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(¤t_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