Merge branch 'js/mingw-shutdown'
authorJunio C Hamano <gitster@pobox.com>
Mon, 23 May 2011 17:27:12 +0000 (10:27 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 23 May 2011 17:27:12 +0000 (10:27 -0700)
* js/mingw-shutdown:
Windows: add a wrapper for the shutdown() system call

1  2 
compat/mingw.c
compat/mingw.h
diff --combined compat/mingw.c
index 4423961768b7389e07090ba8531e086f21d678cf,1cbc9e83d460804e4cafd2c76025ad0d42d81a3e..f6e9ff7762356e099d2fe20fd31359bc0a1f68a2
@@@ -2,9 -2,6 +2,9 @@@
  #include "win32.h"
  #include <conio.h>
  #include "../strbuf.h"
 +#include "../run-command.h"
 +
 +static const int delay[] = { 0, 1, 10, 20, 40 };
  
  int err_win_to_posix(DWORD winerr)
  {
        return error;
  }
  
 +static inline int is_file_in_use_error(DWORD errcode)
 +{
 +      switch (errcode) {
 +      case ERROR_SHARING_VIOLATION:
 +      case ERROR_ACCESS_DENIED:
 +              return 1;
 +      }
 +
 +      return 0;
 +}
 +
 +static int read_yes_no_answer(void)
 +{
 +      char answer[1024];
 +
 +      if (fgets(answer, sizeof(answer), stdin)) {
 +              size_t answer_len = strlen(answer);
 +              int got_full_line = 0, c;
 +
 +              /* remove the newline */
 +              if (answer_len >= 2 && answer[answer_len-2] == '\r') {
 +                      answer[answer_len-2] = '\0';
 +                      got_full_line = 1;
 +              } else if (answer_len >= 1 && answer[answer_len-1] == '\n') {
 +                      answer[answer_len-1] = '\0';
 +                      got_full_line = 1;
 +              }
 +              /* flush the buffer in case we did not get the full line */
 +              if (!got_full_line)
 +                      while ((c = getchar()) != EOF && c != '\n')
 +                              ;
 +      } else
 +              /* we could not read, return the
 +               * default answer which is no */
 +              return 0;
 +
 +      if (tolower(answer[0]) == 'y' && !answer[1])
 +              return 1;
 +      if (!strncasecmp(answer, "yes", sizeof(answer)))
 +              return 1;
 +      if (tolower(answer[0]) == 'n' && !answer[1])
 +              return 0;
 +      if (!strncasecmp(answer, "no", sizeof(answer)))
 +              return 0;
 +
 +      /* did not find an answer we understand */
 +      return -1;
 +}
 +
 +static int ask_yes_no_if_possible(const char *format, ...)
 +{
 +      char question[4096];
 +      const char *retry_hook[] = { NULL, NULL, NULL };
 +      va_list args;
 +
 +      va_start(args, format);
 +      vsnprintf(question, sizeof(question), format, args);
 +      va_end(args);
 +
 +      if ((retry_hook[0] = getenv("GIT_ASK_YESNO"))) {
 +              retry_hook[1] = question;
 +              return !run_command_v_opt(retry_hook, 0);
 +      }
 +
 +      if (!isatty(_fileno(stdin)) || !isatty(_fileno(stderr)))
 +              return 0;
 +
 +      while (1) {
 +              int answer;
 +              fprintf(stderr, "%s (y/n) ", question);
 +
 +              if ((answer = read_yes_no_answer()) >= 0)
 +                      return answer;
 +
 +              fprintf(stderr, "Sorry, I did not understand your answer. "
 +                              "Please type 'y' or 'n'\n");
 +      }
 +}
 +
 +#undef unlink
 +int mingw_unlink(const char *pathname)
 +{
 +      int ret, tries = 0;
 +
 +      /* read-only files cannot be removed */
 +      chmod(pathname, 0666);
 +      while ((ret = unlink(pathname)) == -1 && tries < ARRAY_SIZE(delay)) {
 +              if (!is_file_in_use_error(GetLastError()))
 +                      break;
 +              /*
 +               * We assume that some other process had the source or
 +               * destination file open at the wrong moment and retry.
 +               * In order to give the other process a higher chance to
 +               * complete its operation, we give up our time slice now.
 +               * If we have to retry again, we do sleep a bit.
 +               */
 +              Sleep(delay[tries]);
 +              tries++;
 +      }
 +      while (ret == -1 && is_file_in_use_error(GetLastError()) &&
 +             ask_yes_no_if_possible("Unlink of file '%s' failed. "
 +                      "Should I try again?", pathname))
 +             ret = unlink(pathname);
 +      return ret;
 +}
 +
 +static int is_dir_empty(const char *path)
 +{
 +      struct strbuf buf = STRBUF_INIT;
 +      WIN32_FIND_DATAA findbuf;
 +      HANDLE handle;
 +
 +      strbuf_addf(&buf, "%s\\*", path);
 +      handle = FindFirstFileA(buf.buf, &findbuf);
 +      if (handle == INVALID_HANDLE_VALUE) {
 +              strbuf_release(&buf);
 +              return GetLastError() == ERROR_NO_MORE_FILES;
 +      }
 +
 +      while (!strcmp(findbuf.cFileName, ".") ||
 +                      !strcmp(findbuf.cFileName, ".."))
 +              if (!FindNextFile(handle, &findbuf)) {
 +                      strbuf_release(&buf);
 +                      return GetLastError() == ERROR_NO_MORE_FILES;
 +              }
 +      FindClose(handle);
 +      strbuf_release(&buf);
 +      return 0;
 +}
 +
 +#undef rmdir
 +int mingw_rmdir(const char *pathname)
 +{
 +      int ret, tries = 0;
 +
 +      while ((ret = rmdir(pathname)) == -1 && tries < ARRAY_SIZE(delay)) {
 +              if (!is_file_in_use_error(GetLastError()))
 +                      break;
 +              if (!is_dir_empty(pathname)) {
 +                      errno = ENOTEMPTY;
 +                      break;
 +              }
 +              /*
 +               * We assume that some other process had the source or
 +               * destination file open at the wrong moment and retry.
 +               * In order to give the other process a higher chance to
 +               * complete its operation, we give up our time slice now.
 +               * If we have to retry again, we do sleep a bit.
 +               */
 +              Sleep(delay[tries]);
 +              tries++;
 +      }
 +      while (ret == -1 && is_file_in_use_error(GetLastError()) &&
 +             ask_yes_no_if_possible("Deletion of directory '%s' failed. "
 +                      "Should I try again?", pathname))
 +             ret = rmdir(pathname);
 +      return ret;
 +}
 +
  #undef open
  int mingw_open (const char *filename, int oflags, ...)
  {
@@@ -1130,7 -968,7 +1130,7 @@@ char **make_augmented_environ(const cha
  
  /*
   * Note, this isn't a complete replacement for getaddrinfo. It assumes
 - * that service contains a numerical port, or that it it is null. It
 + * that service contains a numerical port, or that it is null. It
   * does a simple search using gethostbyname, and returns one IPv4 host
   * if one was found.
   */
@@@ -1381,6 -1219,13 +1381,13 @@@ int mingw_setsockopt(int sockfd, int lv
        return setsockopt(s, lvl, optname, (const char*)optval, optlen);
  }
  
+ #undef shutdown
+ int mingw_shutdown(int sockfd, int how)
+ {
+       SOCKET s = (SOCKET)_get_osfhandle(sockfd);
+       return shutdown(s, how);
+ }
  #undef listen
  int mingw_listen(int sockfd, int backlog)
  {
@@@ -1411,6 -1256,7 +1418,6 @@@ int mingw_rename(const char *pold, cons
  {
        DWORD attrs, gle;
        int tries = 0;
 -      static const int delay[] = { 0, 1, 10, 20, 40 };
  
        /*
         * Try native rename() first to get errno right.
@@@ -1452,11 -1298,6 +1459,11 @@@ repeat
                tries++;
                goto repeat;
        }
 +      if (gle == ERROR_ACCESS_DENIED &&
 +             ask_yes_no_if_possible("Rename from '%s' to '%s' failed. "
 +                     "Should I try again?", pold, pnew))
 +              goto repeat;
 +
        errno = EACCES;
        return -1;
  }
diff --combined compat/mingw.h
index 62eccd33911e2f332dc9d0faf097ecff6d6a7aa6,3b20fa979935411232d2ee986845079641e53ac6..547568b9181d61d50f06cf5b4b0ab43af78e5aa2
@@@ -119,6 -119,14 +119,6 @@@ static inline int mingw_mkdir(const cha
  }
  #define mkdir mingw_mkdir
  
 -static inline int mingw_unlink(const char *pathname)
 -{
 -      /* read-only files cannot be removed */
 -      chmod(pathname, 0666);
 -      return unlink(pathname);
 -}
 -#define unlink mingw_unlink
 -
  #define WNOHANG 1
  pid_t waitpid(pid_t pid, int *status, unsigned options);
  
@@@ -166,12 -174,6 +166,12 @@@ int link(const char *oldpath, const cha
   * replacements of existing functions
   */
  
 +int mingw_unlink(const char *pathname);
 +#define unlink mingw_unlink
 +
 +int mingw_rmdir(const char *path);
 +#define rmdir mingw_rmdir
 +
  int mingw_open (const char *filename, int oflags, ...);
  #define open mingw_open
  
@@@ -217,6 -219,9 +217,9 @@@ int mingw_bind(int sockfd, struct socka
  int mingw_setsockopt(int sockfd, int lvl, int optname, void *optval, int optlen);
  #define setsockopt mingw_setsockopt
  
+ int mingw_shutdown(int sockfd, int how);
+ #define shutdown mingw_shutdown
  int mingw_listen(int sockfd, int backlog);
  #define listen mingw_listen