diff options
Diffstat (limited to 'libbb/mingw.c')
-rw-r--r-- | libbb/mingw.c | 1141 |
1 files changed, 1141 insertions, 0 deletions
diff --git a/libbb/mingw.c b/libbb/mingw.c new file mode 100644 index 000000000..2839d9df6 --- /dev/null +++ b/libbb/mingw.c | |||
@@ -0,0 +1,1141 @@ | |||
1 | #include "../git-compat-util.h" | ||
2 | #include "win32.h" | ||
3 | #include "../strbuf.h" | ||
4 | |||
5 | unsigned int _CRT_fmode = _O_BINARY; | ||
6 | |||
7 | static int err_win_to_posix(DWORD winerr) | ||
8 | { | ||
9 | int error = ENOSYS; | ||
10 | switch(winerr) { | ||
11 | case ERROR_ACCESS_DENIED: error = EACCES; break; | ||
12 | case ERROR_ACCOUNT_DISABLED: error = EACCES; break; | ||
13 | case ERROR_ACCOUNT_RESTRICTION: error = EACCES; break; | ||
14 | case ERROR_ALREADY_ASSIGNED: error = EBUSY; break; | ||
15 | case ERROR_ALREADY_EXISTS: error = EEXIST; break; | ||
16 | case ERROR_ARITHMETIC_OVERFLOW: error = ERANGE; break; | ||
17 | case ERROR_BAD_COMMAND: error = EIO; break; | ||
18 | case ERROR_BAD_DEVICE: error = ENODEV; break; | ||
19 | case ERROR_BAD_DRIVER_LEVEL: error = ENXIO; break; | ||
20 | case ERROR_BAD_EXE_FORMAT: error = ENOEXEC; break; | ||
21 | case ERROR_BAD_FORMAT: error = ENOEXEC; break; | ||
22 | case ERROR_BAD_LENGTH: error = EINVAL; break; | ||
23 | case ERROR_BAD_PATHNAME: error = ENOENT; break; | ||
24 | case ERROR_BAD_PIPE: error = EPIPE; break; | ||
25 | case ERROR_BAD_UNIT: error = ENODEV; break; | ||
26 | case ERROR_BAD_USERNAME: error = EINVAL; break; | ||
27 | case ERROR_BROKEN_PIPE: error = EPIPE; break; | ||
28 | case ERROR_BUFFER_OVERFLOW: error = ENAMETOOLONG; break; | ||
29 | case ERROR_BUSY: error = EBUSY; break; | ||
30 | case ERROR_BUSY_DRIVE: error = EBUSY; break; | ||
31 | case ERROR_CALL_NOT_IMPLEMENTED: error = ENOSYS; break; | ||
32 | case ERROR_CANNOT_MAKE: error = EACCES; break; | ||
33 | case ERROR_CANTOPEN: error = EIO; break; | ||
34 | case ERROR_CANTREAD: error = EIO; break; | ||
35 | case ERROR_CANTWRITE: error = EIO; break; | ||
36 | case ERROR_CRC: error = EIO; break; | ||
37 | case ERROR_CURRENT_DIRECTORY: error = EACCES; break; | ||
38 | case ERROR_DEVICE_IN_USE: error = EBUSY; break; | ||
39 | case ERROR_DEV_NOT_EXIST: error = ENODEV; break; | ||
40 | case ERROR_DIRECTORY: error = EINVAL; break; | ||
41 | case ERROR_DIR_NOT_EMPTY: error = ENOTEMPTY; break; | ||
42 | case ERROR_DISK_CHANGE: error = EIO; break; | ||
43 | case ERROR_DISK_FULL: error = ENOSPC; break; | ||
44 | case ERROR_DRIVE_LOCKED: error = EBUSY; break; | ||
45 | case ERROR_ENVVAR_NOT_FOUND: error = EINVAL; break; | ||
46 | case ERROR_EXE_MARKED_INVALID: error = ENOEXEC; break; | ||
47 | case ERROR_FILENAME_EXCED_RANGE: error = ENAMETOOLONG; break; | ||
48 | case ERROR_FILE_EXISTS: error = EEXIST; break; | ||
49 | case ERROR_FILE_INVALID: error = ENODEV; break; | ||
50 | case ERROR_FILE_NOT_FOUND: error = ENOENT; break; | ||
51 | case ERROR_GEN_FAILURE: error = EIO; break; | ||
52 | case ERROR_HANDLE_DISK_FULL: error = ENOSPC; break; | ||
53 | case ERROR_INSUFFICIENT_BUFFER: error = ENOMEM; break; | ||
54 | case ERROR_INVALID_ACCESS: error = EACCES; break; | ||
55 | case ERROR_INVALID_ADDRESS: error = EFAULT; break; | ||
56 | case ERROR_INVALID_BLOCK: error = EFAULT; break; | ||
57 | case ERROR_INVALID_DATA: error = EINVAL; break; | ||
58 | case ERROR_INVALID_DRIVE: error = ENODEV; break; | ||
59 | case ERROR_INVALID_EXE_SIGNATURE: error = ENOEXEC; break; | ||
60 | case ERROR_INVALID_FLAGS: error = EINVAL; break; | ||
61 | case ERROR_INVALID_FUNCTION: error = ENOSYS; break; | ||
62 | case ERROR_INVALID_HANDLE: error = EBADF; break; | ||
63 | case ERROR_INVALID_LOGON_HOURS: error = EACCES; break; | ||
64 | case ERROR_INVALID_NAME: error = EINVAL; break; | ||
65 | case ERROR_INVALID_OWNER: error = EINVAL; break; | ||
66 | case ERROR_INVALID_PARAMETER: error = EINVAL; break; | ||
67 | case ERROR_INVALID_PASSWORD: error = EPERM; break; | ||
68 | case ERROR_INVALID_PRIMARY_GROUP: error = EINVAL; break; | ||
69 | case ERROR_INVALID_SIGNAL_NUMBER: error = EINVAL; break; | ||
70 | case ERROR_INVALID_TARGET_HANDLE: error = EIO; break; | ||
71 | case ERROR_INVALID_WORKSTATION: error = EACCES; break; | ||
72 | case ERROR_IO_DEVICE: error = EIO; break; | ||
73 | case ERROR_IO_INCOMPLETE: error = EINTR; break; | ||
74 | case ERROR_LOCKED: error = EBUSY; break; | ||
75 | case ERROR_LOCK_VIOLATION: error = EACCES; break; | ||
76 | case ERROR_LOGON_FAILURE: error = EACCES; break; | ||
77 | case ERROR_MAPPED_ALIGNMENT: error = EINVAL; break; | ||
78 | case ERROR_META_EXPANSION_TOO_LONG: error = E2BIG; break; | ||
79 | case ERROR_MORE_DATA: error = EPIPE; break; | ||
80 | case ERROR_NEGATIVE_SEEK: error = ESPIPE; break; | ||
81 | case ERROR_NOACCESS: error = EFAULT; break; | ||
82 | case ERROR_NONE_MAPPED: error = EINVAL; break; | ||
83 | case ERROR_NOT_ENOUGH_MEMORY: error = ENOMEM; break; | ||
84 | case ERROR_NOT_READY: error = EAGAIN; break; | ||
85 | case ERROR_NOT_SAME_DEVICE: error = EXDEV; break; | ||
86 | case ERROR_NO_DATA: error = EPIPE; break; | ||
87 | case ERROR_NO_MORE_SEARCH_HANDLES: error = EIO; break; | ||
88 | case ERROR_NO_PROC_SLOTS: error = EAGAIN; break; | ||
89 | case ERROR_NO_SUCH_PRIVILEGE: error = EACCES; break; | ||
90 | case ERROR_OPEN_FAILED: error = EIO; break; | ||
91 | case ERROR_OPEN_FILES: error = EBUSY; break; | ||
92 | case ERROR_OPERATION_ABORTED: error = EINTR; break; | ||
93 | case ERROR_OUTOFMEMORY: error = ENOMEM; break; | ||
94 | case ERROR_PASSWORD_EXPIRED: error = EACCES; break; | ||
95 | case ERROR_PATH_BUSY: error = EBUSY; break; | ||
96 | case ERROR_PATH_NOT_FOUND: error = ENOENT; break; | ||
97 | case ERROR_PIPE_BUSY: error = EBUSY; break; | ||
98 | case ERROR_PIPE_CONNECTED: error = EPIPE; break; | ||
99 | case ERROR_PIPE_LISTENING: error = EPIPE; break; | ||
100 | case ERROR_PIPE_NOT_CONNECTED: error = EPIPE; break; | ||
101 | case ERROR_PRIVILEGE_NOT_HELD: error = EACCES; break; | ||
102 | case ERROR_READ_FAULT: error = EIO; break; | ||
103 | case ERROR_SEEK: error = EIO; break; | ||
104 | case ERROR_SEEK_ON_DEVICE: error = ESPIPE; break; | ||
105 | case ERROR_SHARING_BUFFER_EXCEEDED: error = ENFILE; break; | ||
106 | case ERROR_SHARING_VIOLATION: error = EACCES; break; | ||
107 | case ERROR_STACK_OVERFLOW: error = ENOMEM; break; | ||
108 | case ERROR_SWAPERROR: error = ENOENT; break; | ||
109 | case ERROR_TOO_MANY_MODULES: error = EMFILE; break; | ||
110 | case ERROR_TOO_MANY_OPEN_FILES: error = EMFILE; break; | ||
111 | case ERROR_UNRECOGNIZED_MEDIA: error = ENXIO; break; | ||
112 | case ERROR_UNRECOGNIZED_VOLUME: error = ENODEV; break; | ||
113 | case ERROR_WAIT_NO_CHILDREN: error = ECHILD; break; | ||
114 | case ERROR_WRITE_FAULT: error = EIO; break; | ||
115 | case ERROR_WRITE_PROTECT: error = EROFS; break; | ||
116 | } | ||
117 | return error; | ||
118 | } | ||
119 | |||
120 | #undef open | ||
121 | int mingw_open (const char *filename, int oflags, ...) | ||
122 | { | ||
123 | va_list args; | ||
124 | unsigned mode; | ||
125 | va_start(args, oflags); | ||
126 | mode = va_arg(args, int); | ||
127 | va_end(args); | ||
128 | |||
129 | if (!strcmp(filename, "/dev/null")) | ||
130 | filename = "nul"; | ||
131 | int fd = open(filename, oflags, mode); | ||
132 | if (fd < 0 && (oflags & O_CREAT) && errno == EACCES) { | ||
133 | DWORD attrs = GetFileAttributes(filename); | ||
134 | if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY)) | ||
135 | errno = EISDIR; | ||
136 | } | ||
137 | return fd; | ||
138 | } | ||
139 | |||
140 | static inline time_t filetime_to_time_t(const FILETIME *ft) | ||
141 | { | ||
142 | long long winTime = ((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime; | ||
143 | winTime -= 116444736000000000LL; /* Windows to Unix Epoch conversion */ | ||
144 | winTime /= 10000000; /* Nano to seconds resolution */ | ||
145 | return (time_t)winTime; | ||
146 | } | ||
147 | |||
148 | /* We keep the do_lstat code in a separate function to avoid recursion. | ||
149 | * When a path ends with a slash, the stat will fail with ENOENT. In | ||
150 | * this case, we strip the trailing slashes and stat again. | ||
151 | */ | ||
152 | static int do_lstat(const char *file_name, struct stat *buf) | ||
153 | { | ||
154 | WIN32_FILE_ATTRIBUTE_DATA fdata; | ||
155 | |||
156 | if (!(errno = get_file_attr(file_name, &fdata))) { | ||
157 | buf->st_ino = 0; | ||
158 | buf->st_gid = 0; | ||
159 | buf->st_uid = 0; | ||
160 | buf->st_nlink = 1; | ||
161 | buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes); | ||
162 | buf->st_size = fdata.nFileSizeLow | | ||
163 | (((off_t)fdata.nFileSizeHigh)<<32); | ||
164 | buf->st_dev = buf->st_rdev = 0; /* not used by Git */ | ||
165 | buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime)); | ||
166 | buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime)); | ||
167 | buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime)); | ||
168 | return 0; | ||
169 | } | ||
170 | return -1; | ||
171 | } | ||
172 | |||
173 | /* We provide our own lstat/fstat functions, since the provided | ||
174 | * lstat/fstat functions are so slow. These stat functions are | ||
175 | * tailored for Git's usage (read: fast), and are not meant to be | ||
176 | * complete. Note that Git stat()s are redirected to mingw_lstat() | ||
177 | * too, since Windows doesn't really handle symlinks that well. | ||
178 | */ | ||
179 | int mingw_lstat(const char *file_name, struct stat *buf) | ||
180 | { | ||
181 | int namelen; | ||
182 | static char alt_name[PATH_MAX]; | ||
183 | |||
184 | if (!do_lstat(file_name, buf)) | ||
185 | return 0; | ||
186 | |||
187 | /* if file_name ended in a '/', Windows returned ENOENT; | ||
188 | * try again without trailing slashes | ||
189 | */ | ||
190 | if (errno != ENOENT) | ||
191 | return -1; | ||
192 | |||
193 | namelen = strlen(file_name); | ||
194 | if (namelen && file_name[namelen-1] != '/') | ||
195 | return -1; | ||
196 | while (namelen && file_name[namelen-1] == '/') | ||
197 | --namelen; | ||
198 | if (!namelen || namelen >= PATH_MAX) | ||
199 | return -1; | ||
200 | |||
201 | memcpy(alt_name, file_name, namelen); | ||
202 | alt_name[namelen] = 0; | ||
203 | return do_lstat(alt_name, buf); | ||
204 | } | ||
205 | |||
206 | #undef fstat | ||
207 | int mingw_fstat(int fd, struct stat *buf) | ||
208 | { | ||
209 | HANDLE fh = (HANDLE)_get_osfhandle(fd); | ||
210 | BY_HANDLE_FILE_INFORMATION fdata; | ||
211 | |||
212 | if (fh == INVALID_HANDLE_VALUE) { | ||
213 | errno = EBADF; | ||
214 | return -1; | ||
215 | } | ||
216 | /* direct non-file handles to MS's fstat() */ | ||
217 | if (GetFileType(fh) != FILE_TYPE_DISK) | ||
218 | return _fstati64(fd, buf); | ||
219 | |||
220 | if (GetFileInformationByHandle(fh, &fdata)) { | ||
221 | buf->st_ino = 0; | ||
222 | buf->st_gid = 0; | ||
223 | buf->st_uid = 0; | ||
224 | buf->st_nlink = 1; | ||
225 | buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes); | ||
226 | buf->st_size = fdata.nFileSizeLow | | ||
227 | (((off_t)fdata.nFileSizeHigh)<<32); | ||
228 | buf->st_dev = buf->st_rdev = 0; /* not used by Git */ | ||
229 | buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime)); | ||
230 | buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime)); | ||
231 | buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime)); | ||
232 | return 0; | ||
233 | } | ||
234 | errno = EBADF; | ||
235 | return -1; | ||
236 | } | ||
237 | |||
238 | static inline void time_t_to_filetime(time_t t, FILETIME *ft) | ||
239 | { | ||
240 | long long winTime = t * 10000000LL + 116444736000000000LL; | ||
241 | ft->dwLowDateTime = winTime; | ||
242 | ft->dwHighDateTime = winTime >> 32; | ||
243 | } | ||
244 | |||
245 | int mingw_utime (const char *file_name, const struct utimbuf *times) | ||
246 | { | ||
247 | FILETIME mft, aft; | ||
248 | int fh, rc; | ||
249 | |||
250 | /* must have write permission */ | ||
251 | if ((fh = open(file_name, O_RDWR | O_BINARY)) < 0) | ||
252 | return -1; | ||
253 | |||
254 | time_t_to_filetime(times->modtime, &mft); | ||
255 | time_t_to_filetime(times->actime, &aft); | ||
256 | if (!SetFileTime((HANDLE)_get_osfhandle(fh), NULL, &aft, &mft)) { | ||
257 | errno = EINVAL; | ||
258 | rc = -1; | ||
259 | } else | ||
260 | rc = 0; | ||
261 | close(fh); | ||
262 | return rc; | ||
263 | } | ||
264 | |||
265 | unsigned int sleep (unsigned int seconds) | ||
266 | { | ||
267 | Sleep(seconds*1000); | ||
268 | return 0; | ||
269 | } | ||
270 | |||
271 | int mkstemp(char *template) | ||
272 | { | ||
273 | char *filename = mktemp(template); | ||
274 | if (filename == NULL) | ||
275 | return -1; | ||
276 | return open(filename, O_RDWR | O_CREAT, 0600); | ||
277 | } | ||
278 | |||
279 | int gettimeofday(struct timeval *tv, void *tz) | ||
280 | { | ||
281 | SYSTEMTIME st; | ||
282 | struct tm tm; | ||
283 | GetSystemTime(&st); | ||
284 | tm.tm_year = st.wYear-1900; | ||
285 | tm.tm_mon = st.wMonth-1; | ||
286 | tm.tm_mday = st.wDay; | ||
287 | tm.tm_hour = st.wHour; | ||
288 | tm.tm_min = st.wMinute; | ||
289 | tm.tm_sec = st.wSecond; | ||
290 | tv->tv_sec = tm_to_time_t(&tm); | ||
291 | if (tv->tv_sec < 0) | ||
292 | return -1; | ||
293 | tv->tv_usec = st.wMilliseconds*1000; | ||
294 | return 0; | ||
295 | } | ||
296 | |||
297 | int pipe(int filedes[2]) | ||
298 | { | ||
299 | int fd; | ||
300 | HANDLE h[2], parent; | ||
301 | |||
302 | if (_pipe(filedes, 8192, 0) < 0) | ||
303 | return -1; | ||
304 | |||
305 | parent = GetCurrentProcess(); | ||
306 | |||
307 | if (!DuplicateHandle (parent, (HANDLE)_get_osfhandle(filedes[0]), | ||
308 | parent, &h[0], 0, FALSE, DUPLICATE_SAME_ACCESS)) { | ||
309 | close(filedes[0]); | ||
310 | close(filedes[1]); | ||
311 | return -1; | ||
312 | } | ||
313 | if (!DuplicateHandle (parent, (HANDLE)_get_osfhandle(filedes[1]), | ||
314 | parent, &h[1], 0, FALSE, DUPLICATE_SAME_ACCESS)) { | ||
315 | close(filedes[0]); | ||
316 | close(filedes[1]); | ||
317 | CloseHandle(h[0]); | ||
318 | return -1; | ||
319 | } | ||
320 | fd = _open_osfhandle((int)h[0], O_NOINHERIT); | ||
321 | if (fd < 0) { | ||
322 | close(filedes[0]); | ||
323 | close(filedes[1]); | ||
324 | CloseHandle(h[0]); | ||
325 | CloseHandle(h[1]); | ||
326 | return -1; | ||
327 | } | ||
328 | close(filedes[0]); | ||
329 | filedes[0] = fd; | ||
330 | fd = _open_osfhandle((int)h[1], O_NOINHERIT); | ||
331 | if (fd < 0) { | ||
332 | close(filedes[0]); | ||
333 | close(filedes[1]); | ||
334 | CloseHandle(h[1]); | ||
335 | return -1; | ||
336 | } | ||
337 | close(filedes[1]); | ||
338 | filedes[1] = fd; | ||
339 | return 0; | ||
340 | } | ||
341 | |||
342 | int poll(struct pollfd *ufds, unsigned int nfds, int timeout) | ||
343 | { | ||
344 | int i, pending; | ||
345 | |||
346 | if (timeout >= 0) { | ||
347 | if (nfds == 0) { | ||
348 | Sleep(timeout); | ||
349 | return 0; | ||
350 | } | ||
351 | return errno = EINVAL, error("poll timeout not supported"); | ||
352 | } | ||
353 | |||
354 | /* When there is only one fd to wait for, then we pretend that | ||
355 | * input is available and let the actual wait happen when the | ||
356 | * caller invokes read(). | ||
357 | */ | ||
358 | if (nfds == 1) { | ||
359 | if (!(ufds[0].events & POLLIN)) | ||
360 | return errno = EINVAL, error("POLLIN not set"); | ||
361 | ufds[0].revents = POLLIN; | ||
362 | return 0; | ||
363 | } | ||
364 | |||
365 | repeat: | ||
366 | pending = 0; | ||
367 | for (i = 0; i < nfds; i++) { | ||
368 | DWORD avail = 0; | ||
369 | HANDLE h = (HANDLE) _get_osfhandle(ufds[i].fd); | ||
370 | if (h == INVALID_HANDLE_VALUE) | ||
371 | return -1; /* errno was set */ | ||
372 | |||
373 | if (!(ufds[i].events & POLLIN)) | ||
374 | return errno = EINVAL, error("POLLIN not set"); | ||
375 | |||
376 | /* this emulation works only for pipes */ | ||
377 | if (!PeekNamedPipe(h, NULL, 0, NULL, &avail, NULL)) { | ||
378 | int err = GetLastError(); | ||
379 | if (err == ERROR_BROKEN_PIPE) { | ||
380 | ufds[i].revents = POLLHUP; | ||
381 | pending++; | ||
382 | } else { | ||
383 | errno = EINVAL; | ||
384 | return error("PeekNamedPipe failed," | ||
385 | " GetLastError: %u", err); | ||
386 | } | ||
387 | } else if (avail) { | ||
388 | ufds[i].revents = POLLIN; | ||
389 | pending++; | ||
390 | } else | ||
391 | ufds[i].revents = 0; | ||
392 | } | ||
393 | if (!pending) { | ||
394 | /* The only times that we spin here is when the process | ||
395 | * that is connected through the pipes is waiting for | ||
396 | * its own input data to become available. But since | ||
397 | * the process (pack-objects) is itself CPU intensive, | ||
398 | * it will happily pick up the time slice that we are | ||
399 | * relinguishing here. | ||
400 | */ | ||
401 | Sleep(0); | ||
402 | goto repeat; | ||
403 | } | ||
404 | return 0; | ||
405 | } | ||
406 | |||
407 | struct tm *gmtime_r(const time_t *timep, struct tm *result) | ||
408 | { | ||
409 | /* gmtime() in MSVCRT.DLL is thread-safe, but not reentrant */ | ||
410 | memcpy(result, gmtime(timep), sizeof(struct tm)); | ||
411 | return result; | ||
412 | } | ||
413 | |||
414 | struct tm *localtime_r(const time_t *timep, struct tm *result) | ||
415 | { | ||
416 | /* localtime() in MSVCRT.DLL is thread-safe, but not reentrant */ | ||
417 | memcpy(result, localtime(timep), sizeof(struct tm)); | ||
418 | return result; | ||
419 | } | ||
420 | |||
421 | #undef getcwd | ||
422 | char *mingw_getcwd(char *pointer, int len) | ||
423 | { | ||
424 | int i; | ||
425 | char *ret = getcwd(pointer, len); | ||
426 | if (!ret) | ||
427 | return ret; | ||
428 | for (i = 0; pointer[i]; i++) | ||
429 | if (pointer[i] == '\\') | ||
430 | pointer[i] = '/'; | ||
431 | return ret; | ||
432 | } | ||
433 | |||
434 | #undef getenv | ||
435 | char *mingw_getenv(const char *name) | ||
436 | { | ||
437 | char *result = getenv(name); | ||
438 | if (!result && !strcmp(name, "TMPDIR")) { | ||
439 | /* on Windows it is TMP and TEMP */ | ||
440 | result = getenv("TMP"); | ||
441 | if (!result) | ||
442 | result = getenv("TEMP"); | ||
443 | } | ||
444 | return result; | ||
445 | } | ||
446 | |||
447 | /* | ||
448 | * See http://msdn2.microsoft.com/en-us/library/17w5ykft(vs.71).aspx | ||
449 | * (Parsing C++ Command-Line Arguments) | ||
450 | */ | ||
451 | static const char *quote_arg(const char *arg) | ||
452 | { | ||
453 | /* count chars to quote */ | ||
454 | int len = 0, n = 0; | ||
455 | int force_quotes = 0; | ||
456 | char *q, *d; | ||
457 | const char *p = arg; | ||
458 | if (!*p) force_quotes = 1; | ||
459 | while (*p) { | ||
460 | if (isspace(*p) || *p == '*' || *p == '?' || *p == '{' || *p == '\'') | ||
461 | force_quotes = 1; | ||
462 | else if (*p == '"') | ||
463 | n++; | ||
464 | else if (*p == '\\') { | ||
465 | int count = 0; | ||
466 | while (*p == '\\') { | ||
467 | count++; | ||
468 | p++; | ||
469 | len++; | ||
470 | } | ||
471 | if (*p == '"') | ||
472 | n += count*2 + 1; | ||
473 | continue; | ||
474 | } | ||
475 | len++; | ||
476 | p++; | ||
477 | } | ||
478 | if (!force_quotes && n == 0) | ||
479 | return arg; | ||
480 | |||
481 | /* insert \ where necessary */ | ||
482 | d = q = xmalloc(len+n+3); | ||
483 | *d++ = '"'; | ||
484 | while (*arg) { | ||
485 | if (*arg == '"') | ||
486 | *d++ = '\\'; | ||
487 | else if (*arg == '\\') { | ||
488 | int count = 0; | ||
489 | while (*arg == '\\') { | ||
490 | count++; | ||
491 | *d++ = *arg++; | ||
492 | } | ||
493 | if (*arg == '"') { | ||
494 | while (count-- > 0) | ||
495 | *d++ = '\\'; | ||
496 | *d++ = '\\'; | ||
497 | } | ||
498 | } | ||
499 | *d++ = *arg++; | ||
500 | } | ||
501 | *d++ = '"'; | ||
502 | *d++ = 0; | ||
503 | return q; | ||
504 | } | ||
505 | |||
506 | static const char *parse_interpreter(const char *cmd) | ||
507 | { | ||
508 | static char buf[100]; | ||
509 | char *p, *opt; | ||
510 | int n, fd; | ||
511 | |||
512 | /* don't even try a .exe */ | ||
513 | n = strlen(cmd); | ||
514 | if (n >= 4 && !strcasecmp(cmd+n-4, ".exe")) | ||
515 | return NULL; | ||
516 | |||
517 | fd = open(cmd, O_RDONLY); | ||
518 | if (fd < 0) | ||
519 | return NULL; | ||
520 | n = read(fd, buf, sizeof(buf)-1); | ||
521 | close(fd); | ||
522 | if (n < 4) /* at least '#!/x' and not error */ | ||
523 | return NULL; | ||
524 | |||
525 | if (buf[0] != '#' || buf[1] != '!') | ||
526 | return NULL; | ||
527 | buf[n] = '\0'; | ||
528 | p = strchr(buf, '\n'); | ||
529 | if (!p) | ||
530 | return NULL; | ||
531 | |||
532 | *p = '\0'; | ||
533 | if (!(p = strrchr(buf+2, '/')) && !(p = strrchr(buf+2, '\\'))) | ||
534 | return NULL; | ||
535 | /* strip options */ | ||
536 | if ((opt = strchr(p+1, ' '))) | ||
537 | *opt = '\0'; | ||
538 | return p+1; | ||
539 | } | ||
540 | |||
541 | /* | ||
542 | * Splits the PATH into parts. | ||
543 | */ | ||
544 | static char **get_path_split(void) | ||
545 | { | ||
546 | char *p, **path, *envpath = getenv("PATH"); | ||
547 | int i, n = 0; | ||
548 | |||
549 | if (!envpath || !*envpath) | ||
550 | return NULL; | ||
551 | |||
552 | envpath = xstrdup(envpath); | ||
553 | p = envpath; | ||
554 | while (p) { | ||
555 | char *dir = p; | ||
556 | p = strchr(p, ';'); | ||
557 | if (p) *p++ = '\0'; | ||
558 | if (*dir) { /* not earlier, catches series of ; */ | ||
559 | ++n; | ||
560 | } | ||
561 | } | ||
562 | if (!n) | ||
563 | return NULL; | ||
564 | |||
565 | path = xmalloc((n+1)*sizeof(char*)); | ||
566 | p = envpath; | ||
567 | i = 0; | ||
568 | do { | ||
569 | if (*p) | ||
570 | path[i++] = xstrdup(p); | ||
571 | p = p+strlen(p)+1; | ||
572 | } while (i < n); | ||
573 | path[i] = NULL; | ||
574 | |||
575 | free(envpath); | ||
576 | |||
577 | return path; | ||
578 | } | ||
579 | |||
580 | static void free_path_split(char **path) | ||
581 | { | ||
582 | if (!path) | ||
583 | return; | ||
584 | |||
585 | char **p = path; | ||
586 | while (*p) | ||
587 | free(*p++); | ||
588 | free(path); | ||
589 | } | ||
590 | |||
591 | /* | ||
592 | * exe_only means that we only want to detect .exe files, but not scripts | ||
593 | * (which do not have an extension) | ||
594 | */ | ||
595 | static char *lookup_prog(const char *dir, const char *cmd, int isexe, int exe_only) | ||
596 | { | ||
597 | char path[MAX_PATH]; | ||
598 | snprintf(path, sizeof(path), "%s/%s.exe", dir, cmd); | ||
599 | |||
600 | if (!isexe && access(path, F_OK) == 0) | ||
601 | return xstrdup(path); | ||
602 | path[strlen(path)-4] = '\0'; | ||
603 | if ((!exe_only || isexe) && access(path, F_OK) == 0) | ||
604 | if (!(GetFileAttributes(path) & FILE_ATTRIBUTE_DIRECTORY)) | ||
605 | return xstrdup(path); | ||
606 | return NULL; | ||
607 | } | ||
608 | |||
609 | /* | ||
610 | * Determines the absolute path of cmd using the the split path in path. | ||
611 | * If cmd contains a slash or backslash, no lookup is performed. | ||
612 | */ | ||
613 | static char *path_lookup(const char *cmd, char **path, int exe_only) | ||
614 | { | ||
615 | char *prog = NULL; | ||
616 | int len = strlen(cmd); | ||
617 | int isexe = len >= 4 && !strcasecmp(cmd+len-4, ".exe"); | ||
618 | |||
619 | if (strchr(cmd, '/') || strchr(cmd, '\\')) | ||
620 | prog = xstrdup(cmd); | ||
621 | |||
622 | while (!prog && *path) | ||
623 | prog = lookup_prog(*path++, cmd, isexe, exe_only); | ||
624 | |||
625 | return prog; | ||
626 | } | ||
627 | |||
628 | static int env_compare(const void *a, const void *b) | ||
629 | { | ||
630 | char *const *ea = a; | ||
631 | char *const *eb = b; | ||
632 | return strcasecmp(*ea, *eb); | ||
633 | } | ||
634 | |||
635 | static pid_t mingw_spawnve(const char *cmd, const char **argv, char **env, | ||
636 | int prepend_cmd) | ||
637 | { | ||
638 | STARTUPINFO si; | ||
639 | PROCESS_INFORMATION pi; | ||
640 | struct strbuf envblk, args; | ||
641 | unsigned flags; | ||
642 | BOOL ret; | ||
643 | |||
644 | /* Determine whether or not we are associated to a console */ | ||
645 | HANDLE cons = CreateFile("CONOUT$", GENERIC_WRITE, | ||
646 | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, | ||
647 | FILE_ATTRIBUTE_NORMAL, NULL); | ||
648 | if (cons == INVALID_HANDLE_VALUE) { | ||
649 | /* There is no console associated with this process. | ||
650 | * Since the child is a console process, Windows | ||
651 | * would normally create a console window. But | ||
652 | * since we'll be redirecting std streams, we do | ||
653 | * not need the console. | ||
654 | * It is necessary to use DETACHED_PROCESS | ||
655 | * instead of CREATE_NO_WINDOW to make ssh | ||
656 | * recognize that it has no console. | ||
657 | */ | ||
658 | flags = DETACHED_PROCESS; | ||
659 | } else { | ||
660 | /* There is already a console. If we specified | ||
661 | * DETACHED_PROCESS here, too, Windows would | ||
662 | * disassociate the child from the console. | ||
663 | * The same is true for CREATE_NO_WINDOW. | ||
664 | * Go figure! | ||
665 | */ | ||
666 | flags = 0; | ||
667 | CloseHandle(cons); | ||
668 | } | ||
669 | memset(&si, 0, sizeof(si)); | ||
670 | si.cb = sizeof(si); | ||
671 | si.dwFlags = STARTF_USESTDHANDLES; | ||
672 | si.hStdInput = (HANDLE) _get_osfhandle(0); | ||
673 | si.hStdOutput = (HANDLE) _get_osfhandle(1); | ||
674 | si.hStdError = (HANDLE) _get_osfhandle(2); | ||
675 | |||
676 | /* concatenate argv, quoting args as we go */ | ||
677 | strbuf_init(&args, 0); | ||
678 | if (prepend_cmd) { | ||
679 | char *quoted = (char *)quote_arg(cmd); | ||
680 | strbuf_addstr(&args, quoted); | ||
681 | if (quoted != cmd) | ||
682 | free(quoted); | ||
683 | } | ||
684 | for (; *argv; argv++) { | ||
685 | char *quoted = (char *)quote_arg(*argv); | ||
686 | if (*args.buf) | ||
687 | strbuf_addch(&args, ' '); | ||
688 | strbuf_addstr(&args, quoted); | ||
689 | if (quoted != *argv) | ||
690 | free(quoted); | ||
691 | } | ||
692 | |||
693 | if (env) { | ||
694 | int count = 0; | ||
695 | char **e, **sorted_env; | ||
696 | |||
697 | for (e = env; *e; e++) | ||
698 | count++; | ||
699 | |||
700 | /* environment must be sorted */ | ||
701 | sorted_env = xmalloc(sizeof(*sorted_env) * (count + 1)); | ||
702 | memcpy(sorted_env, env, sizeof(*sorted_env) * (count + 1)); | ||
703 | qsort(sorted_env, count, sizeof(*sorted_env), env_compare); | ||
704 | |||
705 | strbuf_init(&envblk, 0); | ||
706 | for (e = sorted_env; *e; e++) { | ||
707 | strbuf_addstr(&envblk, *e); | ||
708 | strbuf_addch(&envblk, '\0'); | ||
709 | } | ||
710 | free(sorted_env); | ||
711 | } | ||
712 | |||
713 | memset(&pi, 0, sizeof(pi)); | ||
714 | ret = CreateProcess(cmd, args.buf, NULL, NULL, TRUE, flags, | ||
715 | env ? envblk.buf : NULL, NULL, &si, &pi); | ||
716 | |||
717 | if (env) | ||
718 | strbuf_release(&envblk); | ||
719 | strbuf_release(&args); | ||
720 | |||
721 | if (!ret) { | ||
722 | errno = ENOENT; | ||
723 | return -1; | ||
724 | } | ||
725 | CloseHandle(pi.hThread); | ||
726 | return (pid_t)pi.hProcess; | ||
727 | } | ||
728 | |||
729 | pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env) | ||
730 | { | ||
731 | pid_t pid; | ||
732 | char **path = get_path_split(); | ||
733 | char *prog = path_lookup(cmd, path, 0); | ||
734 | |||
735 | if (!prog) { | ||
736 | errno = ENOENT; | ||
737 | pid = -1; | ||
738 | } | ||
739 | else { | ||
740 | const char *interpr = parse_interpreter(prog); | ||
741 | |||
742 | if (interpr) { | ||
743 | const char *argv0 = argv[0]; | ||
744 | char *iprog = path_lookup(interpr, path, 1); | ||
745 | argv[0] = prog; | ||
746 | if (!iprog) { | ||
747 | errno = ENOENT; | ||
748 | pid = -1; | ||
749 | } | ||
750 | else { | ||
751 | pid = mingw_spawnve(iprog, argv, env, 1); | ||
752 | free(iprog); | ||
753 | } | ||
754 | argv[0] = argv0; | ||
755 | } | ||
756 | else | ||
757 | pid = mingw_spawnve(prog, argv, env, 0); | ||
758 | free(prog); | ||
759 | } | ||
760 | free_path_split(path); | ||
761 | return pid; | ||
762 | } | ||
763 | |||
764 | static int try_shell_exec(const char *cmd, char *const *argv, char **env) | ||
765 | { | ||
766 | const char *interpr = parse_interpreter(cmd); | ||
767 | char **path; | ||
768 | char *prog; | ||
769 | int pid = 0; | ||
770 | |||
771 | if (!interpr) | ||
772 | return 0; | ||
773 | path = get_path_split(); | ||
774 | prog = path_lookup(interpr, path, 1); | ||
775 | if (prog) { | ||
776 | int argc = 0; | ||
777 | const char **argv2; | ||
778 | while (argv[argc]) argc++; | ||
779 | argv2 = xmalloc(sizeof(*argv) * (argc+1)); | ||
780 | argv2[0] = (char *)cmd; /* full path to the script file */ | ||
781 | memcpy(&argv2[1], &argv[1], sizeof(*argv) * argc); | ||
782 | pid = mingw_spawnve(prog, argv2, env, 1); | ||
783 | if (pid >= 0) { | ||
784 | int status; | ||
785 | if (waitpid(pid, &status, 0) < 0) | ||
786 | status = 255; | ||
787 | exit(status); | ||
788 | } | ||
789 | pid = 1; /* indicate that we tried but failed */ | ||
790 | free(prog); | ||
791 | free(argv2); | ||
792 | } | ||
793 | free_path_split(path); | ||
794 | return pid; | ||
795 | } | ||
796 | |||
797 | static void mingw_execve(const char *cmd, char *const *argv, char *const *env) | ||
798 | { | ||
799 | /* check if git_command is a shell script */ | ||
800 | if (!try_shell_exec(cmd, argv, (char **)env)) { | ||
801 | int pid, status; | ||
802 | |||
803 | pid = mingw_spawnve(cmd, (const char **)argv, (char **)env, 0); | ||
804 | if (pid < 0) | ||
805 | return; | ||
806 | if (waitpid(pid, &status, 0) < 0) | ||
807 | status = 255; | ||
808 | exit(status); | ||
809 | } | ||
810 | } | ||
811 | |||
812 | void mingw_execvp(const char *cmd, char *const *argv) | ||
813 | { | ||
814 | char **path = get_path_split(); | ||
815 | char *prog = path_lookup(cmd, path, 0); | ||
816 | |||
817 | if (prog) { | ||
818 | mingw_execve(prog, argv, environ); | ||
819 | free(prog); | ||
820 | } else | ||
821 | errno = ENOENT; | ||
822 | |||
823 | free_path_split(path); | ||
824 | } | ||
825 | |||
826 | char **copy_environ() | ||
827 | { | ||
828 | char **env; | ||
829 | int i = 0; | ||
830 | while (environ[i]) | ||
831 | i++; | ||
832 | env = xmalloc((i+1)*sizeof(*env)); | ||
833 | for (i = 0; environ[i]; i++) | ||
834 | env[i] = xstrdup(environ[i]); | ||
835 | env[i] = NULL; | ||
836 | return env; | ||
837 | } | ||
838 | |||
839 | void free_environ(char **env) | ||
840 | { | ||
841 | int i; | ||
842 | for (i = 0; env[i]; i++) | ||
843 | free(env[i]); | ||
844 | free(env); | ||
845 | } | ||
846 | |||
847 | static int lookup_env(char **env, const char *name, size_t nmln) | ||
848 | { | ||
849 | int i; | ||
850 | |||
851 | for (i = 0; env[i]; i++) { | ||
852 | if (0 == strncmp(env[i], name, nmln) | ||
853 | && '=' == env[i][nmln]) | ||
854 | /* matches */ | ||
855 | return i; | ||
856 | } | ||
857 | return -1; | ||
858 | } | ||
859 | |||
860 | /* | ||
861 | * If name contains '=', then sets the variable, otherwise it unsets it | ||
862 | */ | ||
863 | char **env_setenv(char **env, const char *name) | ||
864 | { | ||
865 | char *eq = strchrnul(name, '='); | ||
866 | int i = lookup_env(env, name, eq-name); | ||
867 | |||
868 | if (i < 0) { | ||
869 | if (*eq) { | ||
870 | for (i = 0; env[i]; i++) | ||
871 | ; | ||
872 | env = xrealloc(env, (i+2)*sizeof(*env)); | ||
873 | env[i] = xstrdup(name); | ||
874 | env[i+1] = NULL; | ||
875 | } | ||
876 | } | ||
877 | else { | ||
878 | free(env[i]); | ||
879 | if (*eq) | ||
880 | env[i] = xstrdup(name); | ||
881 | else | ||
882 | for (; env[i]; i++) | ||
883 | env[i] = env[i+1]; | ||
884 | } | ||
885 | return env; | ||
886 | } | ||
887 | |||
888 | /* this is the first function to call into WS_32; initialize it */ | ||
889 | #undef gethostbyname | ||
890 | struct hostent *mingw_gethostbyname(const char *host) | ||
891 | { | ||
892 | WSADATA wsa; | ||
893 | |||
894 | if (WSAStartup(MAKEWORD(2,2), &wsa)) | ||
895 | die("unable to initialize winsock subsystem, error %d", | ||
896 | WSAGetLastError()); | ||
897 | atexit((void(*)(void)) WSACleanup); | ||
898 | return gethostbyname(host); | ||
899 | } | ||
900 | |||
901 | int mingw_socket(int domain, int type, int protocol) | ||
902 | { | ||
903 | int sockfd; | ||
904 | SOCKET s = WSASocket(domain, type, protocol, NULL, 0, 0); | ||
905 | if (s == INVALID_SOCKET) { | ||
906 | /* | ||
907 | * WSAGetLastError() values are regular BSD error codes | ||
908 | * biased by WSABASEERR. | ||
909 | * However, strerror() does not know about networking | ||
910 | * specific errors, which are values beginning at 38 or so. | ||
911 | * Therefore, we choose to leave the biased error code | ||
912 | * in errno so that _if_ someone looks up the code somewhere, | ||
913 | * then it is at least the number that are usually listed. | ||
914 | */ | ||
915 | errno = WSAGetLastError(); | ||
916 | return -1; | ||
917 | } | ||
918 | /* convert into a file descriptor */ | ||
919 | if ((sockfd = _open_osfhandle(s, O_RDWR|O_BINARY)) < 0) { | ||
920 | closesocket(s); | ||
921 | return error("unable to make a socket file descriptor: %s", | ||
922 | strerror(errno)); | ||
923 | } | ||
924 | return sockfd; | ||
925 | } | ||
926 | |||
927 | #undef connect | ||
928 | int mingw_connect(int sockfd, struct sockaddr *sa, size_t sz) | ||
929 | { | ||
930 | SOCKET s = (SOCKET)_get_osfhandle(sockfd); | ||
931 | return connect(s, sa, sz); | ||
932 | } | ||
933 | |||
934 | #undef rename | ||
935 | int mingw_rename(const char *pold, const char *pnew) | ||
936 | { | ||
937 | DWORD attrs; | ||
938 | |||
939 | /* | ||
940 | * Try native rename() first to get errno right. | ||
941 | * It is based on MoveFile(), which cannot overwrite existing files. | ||
942 | */ | ||
943 | if (!rename(pold, pnew)) | ||
944 | return 0; | ||
945 | if (errno != EEXIST) | ||
946 | return -1; | ||
947 | if (MoveFileEx(pold, pnew, MOVEFILE_REPLACE_EXISTING)) | ||
948 | return 0; | ||
949 | /* TODO: translate more errors */ | ||
950 | if (GetLastError() == ERROR_ACCESS_DENIED && | ||
951 | (attrs = GetFileAttributes(pnew)) != INVALID_FILE_ATTRIBUTES) { | ||
952 | if (attrs & FILE_ATTRIBUTE_DIRECTORY) { | ||
953 | errno = EISDIR; | ||
954 | return -1; | ||
955 | } | ||
956 | if ((attrs & FILE_ATTRIBUTE_READONLY) && | ||
957 | SetFileAttributes(pnew, attrs & ~FILE_ATTRIBUTE_READONLY)) { | ||
958 | if (MoveFileEx(pold, pnew, MOVEFILE_REPLACE_EXISTING)) | ||
959 | return 0; | ||
960 | /* revert file attributes on failure */ | ||
961 | SetFileAttributes(pnew, attrs); | ||
962 | } | ||
963 | } | ||
964 | errno = EACCES; | ||
965 | return -1; | ||
966 | } | ||
967 | |||
968 | struct passwd *getpwuid(int uid) | ||
969 | { | ||
970 | static char user_name[100]; | ||
971 | static struct passwd p; | ||
972 | |||
973 | DWORD len = sizeof(user_name); | ||
974 | if (!GetUserName(user_name, &len)) | ||
975 | return NULL; | ||
976 | p.pw_name = user_name; | ||
977 | p.pw_gecos = "unknown"; | ||
978 | p.pw_dir = NULL; | ||
979 | return &p; | ||
980 | } | ||
981 | |||
982 | static HANDLE timer_event; | ||
983 | static HANDLE timer_thread; | ||
984 | static int timer_interval; | ||
985 | static int one_shot; | ||
986 | static sig_handler_t timer_fn = SIG_DFL; | ||
987 | |||
988 | /* The timer works like this: | ||
989 | * The thread, ticktack(), is a trivial routine that most of the time | ||
990 | * only waits to receive the signal to terminate. The main thread tells | ||
991 | * the thread to terminate by setting the timer_event to the signalled | ||
992 | * state. | ||
993 | * But ticktack() interrupts the wait state after the timer's interval | ||
994 | * length to call the signal handler. | ||
995 | */ | ||
996 | |||
997 | static __stdcall unsigned ticktack(void *dummy) | ||
998 | { | ||
999 | while (WaitForSingleObject(timer_event, timer_interval) == WAIT_TIMEOUT) { | ||
1000 | if (timer_fn == SIG_DFL) | ||
1001 | die("Alarm"); | ||
1002 | if (timer_fn != SIG_IGN) | ||
1003 | timer_fn(SIGALRM); | ||
1004 | if (one_shot) | ||
1005 | break; | ||
1006 | } | ||
1007 | return 0; | ||
1008 | } | ||
1009 | |||
1010 | static int start_timer_thread(void) | ||
1011 | { | ||
1012 | timer_event = CreateEvent(NULL, FALSE, FALSE, NULL); | ||
1013 | if (timer_event) { | ||
1014 | timer_thread = (HANDLE) _beginthreadex(NULL, 0, ticktack, NULL, 0, NULL); | ||
1015 | if (!timer_thread ) | ||
1016 | return errno = ENOMEM, | ||
1017 | error("cannot start timer thread"); | ||
1018 | } else | ||
1019 | return errno = ENOMEM, | ||
1020 | error("cannot allocate resources for timer"); | ||
1021 | return 0; | ||
1022 | } | ||
1023 | |||
1024 | static void stop_timer_thread(void) | ||
1025 | { | ||
1026 | if (timer_event) | ||
1027 | SetEvent(timer_event); /* tell thread to terminate */ | ||
1028 | if (timer_thread) { | ||
1029 | int rc = WaitForSingleObject(timer_thread, 1000); | ||
1030 | if (rc == WAIT_TIMEOUT) | ||
1031 | error("timer thread did not terminate timely"); | ||
1032 | else if (rc != WAIT_OBJECT_0) | ||
1033 | error("waiting for timer thread failed: %lu", | ||
1034 | GetLastError()); | ||
1035 | CloseHandle(timer_thread); | ||
1036 | } | ||
1037 | if (timer_event) | ||
1038 | CloseHandle(timer_event); | ||
1039 | timer_event = NULL; | ||
1040 | timer_thread = NULL; | ||
1041 | } | ||
1042 | |||
1043 | static inline int is_timeval_eq(const struct timeval *i1, const struct timeval *i2) | ||
1044 | { | ||
1045 | return i1->tv_sec == i2->tv_sec && i1->tv_usec == i2->tv_usec; | ||
1046 | } | ||
1047 | |||
1048 | int setitimer(int type, struct itimerval *in, struct itimerval *out) | ||
1049 | { | ||
1050 | static const struct timeval zero; | ||
1051 | static int atexit_done; | ||
1052 | |||
1053 | if (out != NULL) | ||
1054 | return errno = EINVAL, | ||
1055 | error("setitimer param 3 != NULL not implemented"); | ||
1056 | if (!is_timeval_eq(&in->it_interval, &zero) && | ||
1057 | !is_timeval_eq(&in->it_interval, &in->it_value)) | ||
1058 | return errno = EINVAL, | ||
1059 | error("setitimer: it_interval must be zero or eq it_value"); | ||
1060 | |||
1061 | if (timer_thread) | ||
1062 | stop_timer_thread(); | ||
1063 | |||
1064 | if (is_timeval_eq(&in->it_value, &zero) && | ||
1065 | is_timeval_eq(&in->it_interval, &zero)) | ||
1066 | return 0; | ||
1067 | |||
1068 | timer_interval = in->it_value.tv_sec * 1000 + in->it_value.tv_usec / 1000; | ||
1069 | one_shot = is_timeval_eq(&in->it_interval, &zero); | ||
1070 | if (!atexit_done) { | ||
1071 | atexit(stop_timer_thread); | ||
1072 | atexit_done = 1; | ||
1073 | } | ||
1074 | return start_timer_thread(); | ||
1075 | } | ||
1076 | |||
1077 | int sigaction(int sig, struct sigaction *in, struct sigaction *out) | ||
1078 | { | ||
1079 | if (sig != SIGALRM) | ||
1080 | return errno = EINVAL, | ||
1081 | error("sigaction only implemented for SIGALRM"); | ||
1082 | if (out != NULL) | ||
1083 | return errno = EINVAL, | ||
1084 | error("sigaction: param 3 != NULL not implemented"); | ||
1085 | |||
1086 | timer_fn = in->sa_handler; | ||
1087 | return 0; | ||
1088 | } | ||
1089 | |||
1090 | #undef signal | ||
1091 | sig_handler_t mingw_signal(int sig, sig_handler_t handler) | ||
1092 | { | ||
1093 | if (sig != SIGALRM) | ||
1094 | return signal(sig, handler); | ||
1095 | sig_handler_t old = timer_fn; | ||
1096 | timer_fn = handler; | ||
1097 | return old; | ||
1098 | } | ||
1099 | |||
1100 | static const char *make_backslash_path(const char *path) | ||
1101 | { | ||
1102 | static char buf[PATH_MAX + 1]; | ||
1103 | char *c; | ||
1104 | |||
1105 | if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX) | ||
1106 | die("Too long path: %.*s", 60, path); | ||
1107 | |||
1108 | for (c = buf; *c; c++) { | ||
1109 | if (*c == '/') | ||
1110 | *c = '\\'; | ||
1111 | } | ||
1112 | return buf; | ||
1113 | } | ||
1114 | |||
1115 | void mingw_open_html(const char *unixpath) | ||
1116 | { | ||
1117 | const char *htmlpath = make_backslash_path(unixpath); | ||
1118 | printf("Launching default browser to display HTML ...\n"); | ||
1119 | ShellExecute(NULL, "open", htmlpath, NULL, "\\", 0); | ||
1120 | } | ||
1121 | |||
1122 | int link(const char *oldpath, const char *newpath) | ||
1123 | { | ||
1124 | typedef BOOL WINAPI (*T)(const char*, const char*, LPSECURITY_ATTRIBUTES); | ||
1125 | static T create_hard_link = NULL; | ||
1126 | if (!create_hard_link) { | ||
1127 | create_hard_link = (T) GetProcAddress( | ||
1128 | GetModuleHandle("kernel32.dll"), "CreateHardLinkA"); | ||
1129 | if (!create_hard_link) | ||
1130 | create_hard_link = (T)-1; | ||
1131 | } | ||
1132 | if (create_hard_link == (T)-1) { | ||
1133 | errno = ENOSYS; | ||
1134 | return -1; | ||
1135 | } | ||
1136 | if (!create_hard_link(newpath, oldpath, NULL)) { | ||
1137 | errno = err_win_to_posix(GetLastError()); | ||
1138 | return -1; | ||
1139 | } | ||
1140 | return 0; | ||
1141 | } | ||