/*
 *	Do the actual making for make
 */

#include "h.h"

/*
 *  Support functions for Windows 95
 */

time_t   CnvSystemTime(LPSYSTEMTIME pSysTime)
{
  time_t     result = 0;
  int        MonthDays[12] = { 31,28,31,30,31,30,31,31,30,31,30,31 };
  int       wYear,wMonth;

  for (wYear = 1970;wYear < pSysTime->wYear;wYear++)
    result += (((wYear % 4)==0) ? 366 : 365);

  for (wMonth = 1;wMonth < pSysTime->wMonth;wMonth++)
    result += MonthDays[wMonth-1];
    
  if ( (wMonth == 2) && ((wYear % 4) == 0) )
    result ++;
  
  result += pSysTime->wDay - 1;
  result *= 24;
  result += pSysTime->wHour;
  result *= 60;
  result += pSysTime->wMinute;
  result *= 60;
  result += pSysTime->wSecond;

#ifndef NDEBUG
  printf("%2d %2d %4d , %2d:%02d:%02d = %d\n",
	 pSysTime->wDay,pSysTime->wMonth,pSysTime->wYear,
	 pSysTime->wHour,pSysTime->wMinute,pSysTime->wSecond,
	 result);
#endif

  return result;
}

time_t time(time_t *buf)
{
  SYSTEMTIME stNow;
  time_t     result;

  GetSystemTime(&stNow);
  result = CnvSystemTime(&stNow);
  if (buf)
    *buf = result;
  return result;
}

char *strdup(const char *orig)
{
  char *result;

  result = (char *)malloc(strlen(orig)+1);
  if (result != NULL)
    strcpy(result,orig);
  return result;
}

/*
 * This way, child programs will execute and return the
 * right exit code values.
 */

int _system(const char *cmd)
{
  BOOL                bResult;
  STARTUPINFO         si;
  PROCESS_INFORMATION pi;
  DWORD               exitcode;

  memset(&si,0,sizeof(STARTUPINFO));
  si.cb = sizeof(STARTUPINFO);
  
  bResult = CreateProcess(NULL,
			  cmd,
			  NULL,NULL, /* No security */
			  TRUE,	     /* Inherits handles */
			  0,	     /* Runs normally  */
			  NULL,NULL, /* Inherits env & cwd */
			  &si,&pi);

  if (bResult == FALSE) 
    return system(cmd);
  /* Modification proposed by Sune Falck, Sune.Falck@swipnet.se */
  WaitForSingleObject(pi.hProcess,INFINITE);
  if (GetExitCodeProcess(pi.hProcess,&exitcode)==FALSE) {
      fprintf(stderr,"Impossible to get the exit status for %s\n",cmd);
      return -1;
   }

  return (int)exitcode;
}


/*
 *	Do commands to make a target
 */
void docmds1(NAME *np,LINE *lp)
{
  bool	        ssilent;
  bool	        signore;
  int	        estat;
  register char *q;
  register CMD  *cp;
  
  for (cp = lp->l_cmd; cp; cp = cp->c_next)
    {
      strcpy(str1, cp->c_cmd);
      expand(str1);
      q = str1;
      ssilent = silent;
      signore = ignore;
      while ((*q == '@') || (*q == '-'))
	{
	  if (*q == '@')	   /*  Specific silent  */
	    ssilent = TRUE;
	  else		   /*  Specific ignore  */
	    signore = TRUE;
	  q++;		   /*  Not part of the command  */
	}
      
      if (!domake)
	ssilent = 0;
      
      if (!ssilent)
	printf("   %s\n",q);
      
      if (domake)
	{			/*  Get the shell to execute it  */
	  estat = _system(q);
	  if (estat != 0)
	    {
	      printf("%s: Error code %d", myname, estat);
	      if (signore)
		fputs(" (Ignored)\n", stdout);
	      else
		{
		  putchar('\n');
		  if (!(np->n_flag & N_PREC))
		    if (unlink(np->n_name) == 0)
		      printf("%s: '%s' removed.\n", myname, np->n_name);
		  exit(estat);
		}
	    }
	}
    }
}


void docmds(NAME *np)
{
  register LINE *lp;
  
  
  for (lp = np->n_line; lp; lp = lp->l_next)
    docmds1(np, lp);
}


/*
 *	Get the modification time of a file.  If the first
 *	doesn't exist, it's modtime is set to 0.
 */
void modtime(struct name *np)
{
  FILETIME LastModifyTime;
  SYSTEMTIME LastModSysTime;
  HANDLE   hFile;
  BOOL     bResult;

  np->n_time = 0L;
  hFile = CreateFileA(np->n_name,GENERIC_READ,
		      FILE_SHARE_READ, NULL, OPEN_EXISTING,
		      FILE_ATTRIBUTE_NORMAL,NULL);

  if (hFile == INVALID_HANDLE_VALUE) {
    DWORD LastError = GetLastError();
    if (LastError != ERROR_FILE_NOT_FOUND) 
      fatal ("Couldn't open %s GetLastError() = %d",np->n_name,LastError);
    return;
  }
  bResult = GetFileTime(hFile,NULL,NULL,&LastModifyTime);
  CloseHandle(hFile);
  if (bResult == FALSE)
    fatal ("Couldn't get file time for %s\n",np->n_name);
  FileTimeToSystemTime(&LastModifyTime,&LastModSysTime);
  np->n_time = CnvSystemTime(&LastModSysTime);
}


/*
 *	Update the mod time of a file to now.
 */
void touch(struct name *np)
{
  SYSTEMTIME stNow;
  FILETIME   ftNow;
  HANDLE     hFile;

  if (!domake || !silent)
    printf("    touch(%s)\n", np->n_name);
  
  if (domake)
    {
      hFile = CreateFileA(np->n_name,GENERIC_WRITE,
			  0,NULL,OPEN_EXISTING,
			  FILE_ATTRIBUTE_NORMAL,NULL);

      if (hFile == INVALID_HANDLE_VALUE) {
	fatal("Couldn't touch %s",np->n_name);
      }
      GetSystemTime(&stNow);
      SystemTimeToFileTime(&stNow,&ftNow);
      SetFileTime(hFile,&ftNow,&ftNow,&ftNow);
      CloseHandle(hFile);
    }
}


/*
 *	Recursive routine to make a target.
 */
int make(NAME *np,int level)
{
  register DEPEND *dp;
  register LINE   *lp;
  register struct depend *qdp;
  time_t		 dtime = 1;
  bool			 didsomething = 0;
  
  if (np->n_flag & N_DONE)
    return 0;
  
  if (!np->n_time)
    modtime(np);		/*  Gets modtime of this file  */
  
  if (rules)
    {
      for (lp = np->n_line; lp; lp = lp->l_next)
	if (lp->l_cmd)
	  break;
      if (!lp)
	dyndep(np);
    }
  
  if ( ((np->n_flag & N_TARG) == 0) && (np->n_time == 0L) )
    fatal("Don't know how to make %s", np->n_name);
  
  for (qdp = (DEPEND *)0, lp = np->n_line; lp; lp = lp->l_next)
    {
      for (dp = lp->l_dep; dp; dp = dp->d_next)
	{
	  make(dp->d_name, level+1);
	  if (np->n_time < dp->d_name->n_time)
	    qdp = newdep(dp->d_name, qdp);
	  dtime = max(dtime, dp->d_name->n_time);
	}
      if (!quest && (np->n_flag & N_DOUBLE) && (np->n_time < dtime))
	{
	  make1(np, lp, qdp);	/* free()'s qdp */
	  dtime = 1;
	  qdp = (DEPEND *)0;
	  didsomething++;
	}
    }
  
  np->n_flag |= N_DONE;
  
  if (quest)
    {
      long		t;
      
      t = np->n_time;
      time(&np->n_time);
      return t < dtime;
    }
  else if (np->n_time < dtime && !(np->n_flag & N_DOUBLE))
    {
      make1(np, (LINE *)0, qdp);	/* free()'s qdp */
      time(&np->n_time);
    }
  else if (level == 0 && !didsomething)
    printf("%s: '%s' is up to date\n", myname, np->n_name);
  return 0;
}


make1(np, lp, qdp)
register DEPEND *	qdp;
LINE *			lp;
NAME *			np;
{
  register DEPEND *	dp;


  if (dotouch)
    touch(np);
  else
    {
      strcpy(str1, "");
      for (dp = qdp; dp; dp = qdp)
	{
	  if (strlen(str1))
	    strcat(str1, " ");
	  strcat(str1, dp->d_name->n_name);
	  qdp = dp->d_next;
	  free(dp);
	}
      setmacro("?", str1);
      setmacro("@", np->n_name);

      /* R.D. 1987/02/01  add "$*" macro to stand for target minus suffix */
      /* I'm assuming that np->n_name is the full name of the target. */

      {
#if 1
#define	MAXNAMSIZ	32		/* allow 32-char names */
	char tmpnam[MAXNAMSIZ];
	char *p;
	strcpy(tmpnam, np->n_name);
	p = tmpnam + strlen(tmpnam);
	while (*p != '.' && p != tmpnam)
	  --p;
	/* now p points to dot, or tmpnam, or both */
	if (*p == '.')
	  *p = '\0';		/* null out extension */
	
	/* now tmpnam holds target minus suffix */
	setmacro("*", tmpnam);
#else
	char *ntarget = strdup(np->n_name);
	char *p       = strrchr(ntarget,'.');
	if (*p)
	  p = '\0';
	setmacro("*",ntarget);
#endif
      }
 
      if (lp)		/* lp set if doing a :: rule */
	docmds1(np, lp);
      else
	docmds(np);
    }
}

