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