/* Utility and Unix shadow routines for GNU Emacs support programs on NT.
Copyright (C) 1994, 2001-2017 Free Software Foundation, Inc.
Author: Geoff Voelker (voelker@cs.washington.edu)
Created: 10-8-94
This file is part of GNU Emacs.
GNU Emacs is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at
your option) any later version.
GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Emacs.  If not, see .  */
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "ntlib.h"
char *sys_ctime (const time_t *);
FILE *sys_fopen (const char *, const char *);
int sys_chdir (const char *);
int mkostemp (char *, int);
int sys_rename (const char *, const char *);
/* MinGW64 defines _TIMEZONE_DEFINED and defines 'struct timespec' in
   its system headers.  */
#ifndef _TIMEZONE_DEFINED
struct timezone
{
  int		tz_minuteswest;	/* minutes west of Greenwich */
  int		tz_dsttime;	/* type of dst correction */
};
#endif
#define MAXPATHLEN _MAX_PATH
/* Emulate sleep...we could have done this with a define, but that
   would necessitate including windows.h in the files that used it.
   This is much easier.  */
unsigned
sleep (unsigned seconds)
{
  Sleep (seconds * 1000);
  return 0;
}
/* Get the current working directory.  */
char *
getwd (char *dir)
{
  if (GetCurrentDirectory (MAXPATHLEN, dir) > 0)
    return dir;
  return NULL;
}
static HANDLE getppid_parent;
static int    getppid_ppid;
int
getppid (void)
{
  char *ppid;
  DWORD result;
  ppid = getenv ("EM_PARENT_PROCESS_ID");
  if (!ppid)
    {
      printf ("no pid.\n");
      return 0;
    }
  else
    {
      getppid_ppid = atoi (ppid);
    }
  if (!getppid_parent)
    {
      getppid_parent = OpenProcess (SYNCHRONIZE, FALSE, atoi (ppid));
      if (!getppid_parent)
	{
	  printf ("Failed to open handle to parent process: %lu\n",
		 GetLastError ());
	  exit (1);
	}
    }
  result = WaitForSingleObject (getppid_parent, 0);
  switch (result)
    {
    case WAIT_TIMEOUT:
      /* The parent is still alive.  */
      return getppid_ppid;
    case WAIT_OBJECT_0:
      /* The parent is gone.  Return the pid of Unix init (1).  */
      return 1;
    case WAIT_FAILED:
    default:
      printf ("Checking parent status failed: %lu\n", GetLastError ());
      exit (1);
    }
}
char *
getlogin (void)
{
  static char user_name[256];
  DWORD  length = sizeof (user_name);
  if (GetUserName (user_name, &length))
    return user_name;
  return NULL;
}
char *
cuserid (char * s)
{
  char * name = getlogin ();
  if (s)
    return strcpy (s, name ? name : "");
  return name;
}
unsigned
getuid (void)
{
  return 0;
}
unsigned
geteuid (void)
{
  return getuid ();
}
unsigned
getgid (void)
{
  return 0;
}
unsigned
getegid (void)
{
  return 0;
}
int
setuid (unsigned uid)
{
  return 0;
}
int
setregid (unsigned rgid, unsigned gid)
{
  return 0;
}
struct passwd *
getpwuid (unsigned uid)
{
  return NULL;
}
char *
getpass (const char * prompt)
{
  static char input[256];
  HANDLE in;
  HANDLE err;
  DWORD  count;
  in = GetStdHandle (STD_INPUT_HANDLE);
  err = GetStdHandle (STD_ERROR_HANDLE);
  if (in == INVALID_HANDLE_VALUE || err == INVALID_HANDLE_VALUE)
    return NULL;
  if (WriteFile (err, prompt, strlen (prompt), &count, NULL))
    {
      int istty = (GetFileType (in) == FILE_TYPE_CHAR);
      DWORD old_flags;
      int rc;
      if (istty)
	{
	  if (GetConsoleMode (in, &old_flags))
	    SetConsoleMode (in, ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT);
	  else
	    istty = 0;
	}
      rc = ReadFile (in, input, sizeof (input), &count, NULL);
      if (count >= 2 && input[count - 2] == '\r')
	input[count - 2] = '\0';
      else
	{
	  char buf[256];
	  while (ReadFile (in, buf, sizeof (buf), &count, NULL) > 0)
	    if (count >= 2 && buf[count - 2] == '\r')
	      break;
	}
      WriteFile (err, "\r\n", 2, &count, NULL);
      if (istty)
	SetConsoleMode (in, old_flags);
      if (rc)
	return input;
    }
  return NULL;
}
int
fchown (int fd, unsigned uid, unsigned gid)
{
  return 0;
}
FILE *
sys_fopen (const char * path, const char * mode)
{
  return fopen (path, mode);
}
int
sys_chdir (const char * path)
{
  return _chdir (path);
}
static FILETIME utc_base_ft;
static long double utc_base;
static int init = 0;
static time_t
convert_time (FILETIME ft)
{
  long double ret;
  if (CompareFileTime (&ft, &utc_base_ft) < 0)
    return 0;
  ret = (long double) ft.dwHighDateTime
    * 4096.0L * 1024.0L * 1024.0L + ft.dwLowDateTime;
  ret -= utc_base;
  return (time_t) (ret * 1e-7L);
}
static int
is_exec (const char * name)
{
  char * p = strrchr (name, '.');
  return
    (p != NULL
     && (stricmp (p, ".exe") == 0 ||
	 stricmp (p, ".com") == 0 ||
	 stricmp (p, ".bat") == 0 ||
	 stricmp (p, ".cmd") == 0));
}
/* FIXME?  This is in configure.ac now - is this still needed?  */
#define IS_DIRECTORY_SEP(x) ((x) == '/' || (x) == '\\')
/* We need this because nt/inc/sys/stat.h defines struct stat that is
   incompatible with the MS run-time libraries.  */
int
stat (const char * path, struct stat * buf)
{
  WIN32_FIND_DATA wfd;
  HANDLE fh;
  int permission;
  int len;
  int rootdir = FALSE;
  char *name = alloca (FILENAME_MAX);
  if (!init)
    {
      /* Determine the delta between 1-Jan-1601 and 1-Jan-1970. */
      SYSTEMTIME st;
      st.wYear = 1970;
      st.wMonth = 1;
      st.wDay = 1;
      st.wHour = 0;
      st.wMinute = 0;
      st.wSecond = 0;
      st.wMilliseconds = 0;
      SystemTimeToFileTime (&st, &utc_base_ft);
      utc_base = (long double) utc_base_ft.dwHighDateTime
	* 4096.0L * 1024.0L * 1024.0L + utc_base_ft.dwLowDateTime;
      init = 1;
    }
  if (path == NULL || buf == NULL || *path == '\0')
    {
      errno = EFAULT;
      return -1;
    }
  if (_mbspbrk (path, "*?|<>\""))
    {
      errno = ENOENT;
      return -1;
    }
  strcpy (name, path);
  /* Remove trailing directory separator, unless name is the root
     directory of a drive in which case ensure there is a trailing
     separator. */
  len = strlen (name);
  rootdir = IS_DIRECTORY_SEP (name[0])
    || (len == 3 && name[1] == ':' && IS_DIRECTORY_SEP (name[2]));
  if (rootdir)
    {
      if (GetDriveType (name) < 2)
	{
	  errno = ENOENT;
	  return -1;
	}
      memset (&wfd, 0, sizeof (wfd));
      wfd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
      wfd.ftCreationTime = utc_base_ft;
      wfd.ftLastAccessTime = utc_base_ft;
      wfd.ftLastWriteTime = utc_base_ft;
      strcpy (wfd.cFileName, name);
    }
  else
    {
      if (IS_DIRECTORY_SEP (name[len-1]))
	name[len - 1] = 0;
      fh = FindFirstFile (name, &wfd);
      if (fh == INVALID_HANDLE_VALUE)
	{
	  errno = ENOENT;
	  return -1;
	}
      FindClose (fh);
    }
  buf->st_mode = (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ?
    S_IFDIR : S_IFREG;
  buf->st_nlink = 1;
  buf->st_ino = 0;
  if (name[0] && name[1] == ':')
    buf->st_dev = tolower (name[0]) - 'a' + 1;
  else
    buf->st_dev = _getdrive ();
  buf->st_rdev = buf->st_dev;
  buf->st_size = wfd.nFileSizeLow;
  /* Convert timestamps to Unix format. */
  buf->st_mtime = convert_time (wfd.ftLastWriteTime);
  buf->st_atime = convert_time (wfd.ftLastAccessTime);
  if (buf->st_atime == 0) buf->st_atime = buf->st_mtime;
  buf->st_ctime = convert_time (wfd.ftCreationTime);
  if (buf->st_ctime == 0) buf->st_ctime = buf->st_mtime;
  /* determine rwx permissions */
  if (wfd.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
    permission = S_IREAD;
  else
    permission = S_IREAD | S_IWRITE;
  if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
    permission |= S_IEXEC;
  else if (is_exec (name))
    permission |= S_IEXEC;
  buf->st_mode |= permission | (permission >> 3) | (permission >> 6);
  return 0;
}
int
lstat (const char * path, struct stat * buf)
{
  return stat (path, buf);
}
/* Implementation of mkostemp for MS-Windows, to avoid race conditions
   when using mktemp.  Copied from w32.c.
   This is used only in update-game-score.c.  It is overkill for that
   use case, since update-game-score renames the temporary file into
   the game score file, which isn't atomic on MS-Windows anyway, when
   the game score already existed before running the program, which it
   almost always does.  But using a simpler implementation just to
   make a point is uneconomical...  */
int
mkostemp (char * template, int flags)
{
  char * p;
  int i, fd = -1;
  unsigned uid = GetCurrentThreadId ();
  int save_errno = errno;
  static char first_char[] = "abcdefghijklmnopqrstuvwyz0123456789!%-_@#";
  errno = EINVAL;
  if (template == NULL)
    return -1;
  p = template + strlen (template);
  i = 5;
  /* replace up to the last 5 X's with uid in decimal */
  while (--p >= template && p[0] == 'X' && --i >= 0)
    {
      p[0] = '0' + uid % 10;
      uid /= 10;
    }
  if (i < 0 && p[0] == 'X')
    {
      i = 0;
      do
	{
	  p[0] = first_char[i];
	  if ((fd = open (template,
			  flags | _O_CREAT | _O_EXCL | _O_RDWR,
			  S_IRUSR | S_IWUSR)) >= 0
	      || errno != EEXIST)
	    {
	      if (fd >= 0)
		errno = save_errno;
	      return fd;
	    }
	}
      while (++i < sizeof (first_char));
    }
  /* Template is badly formed or else we can't generate a unique name.  */
  return -1;
}
/* On Windows, you cannot rename into an existing file.  */
int
sys_rename (const char *from, const char *to)
{
  int retval = rename (from, to);
  if (retval < 0 && errno == EEXIST)
    {
      if (unlink (to) == 0)
	retval = rename (from, to);
    }
  return retval;
}