pennmush/game/data/
pennmush/game/log/
pennmush/game/save/
pennmush/game/txt/evt/
pennmush/game/txt/nws/
pennmush/os2/
pennmush/po/
pennmush/win32/msvc.net/
pennmush/win32/msvc6/
/* File manipulation routines */
/* Author of Win32-specific bits: Nick Gammon */

#include "copyrite.h"
#include "config.h"

#include <stdlib.h>
#include <stdio.h>

#ifdef WIN32
#include <windows.h>
#include <process.h>
#include <direct.h>
#endif

#ifdef I_UNISTD
#include <unistd.h>
#endif

#include "conf.h"
#include "mushdb.h"
#include "match.h"
#include "externs.h"
#include "mymalloc.h"
#include "log.h"
#include "confmagic.h"

extern char confname[BUFFER_LEN];	/* From bsd.c */

#ifdef WIN32

/* This is bad, but only for WIN32, which is bad anyway... */
#define EMBEDDED_MKINDX
static char buff[1024];
BOOL
ConcatenateFiles(const char *path, const char *outputfile)
{
  HANDLE filscan;
  WIN32_FIND_DATA fildata;
  BOOL filflag;
  DWORD status;
  FILE *fo = NULL;
  FILE *f = NULL;
  size_t bytes_in, bytes_out;
  long total_bytes = 0;
  int total_files = 0;
  char directory[MAX_PATH];
  char fullname[MAX_PATH];
  char *p;

  /* If outputfile is an empty string, forget it. */
  if (!outputfile || !*outputfile)
    return FALSE;

/* extract the directory from the path name */
  strcpy(directory, path);
  p = strrchr(directory, '\\');
  if (p)
    p[1] = 0;

  else {
    p = strrchr(directory, '/');
    if (p)
      p[1] = 0;
  }

/* Open output file */
  fo = fopen(outputfile, "wb");
  if (!fo) {
    do_rawlog(LT_ERR, T("Unable to open file: %s"), outputfile);
    return FALSE;
  }
  do_rawlog(LT_ERR, T("Creating file: %s"), outputfile);

/* Find first file matching the wildcard */
  filscan = FindFirstFile(path, &fildata);
  if (filscan == INVALID_HANDLE_VALUE) {
    status = GetLastError();
    fclose(fo);
    do_rawlog(LT_ERR, "**** No files matching: \"%s\" found.", path);
    if (status == ERROR_NO_MORE_FILES)
      return TRUE;

    else
      return FALSE;
  }

/*
   Now enter the concatenation loop.
 */

  do {
    if (!(fildata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
      do_rawlog(LT_ERR, "%s: %s, %ld %s", T("    Copying file"),
		fildata.cFileName, fildata.nFileSizeLow,
		fildata.nFileSizeLow == 1 ? T("byte") : T("bytes"));
      strcpy(fullname, directory);
      strcat(fullname, fildata.cFileName);

/* Open the input file */
      f = fopen(fullname, "rb");
      if (!f)
	do_rawlog(LT_ERR, T("    ** Unable to open file: %s"), fullname);

      else {
	total_files++;

	/* do the copy loop */
	while (!feof(f)) {
	  bytes_in = fread(buff, 1, sizeof(buff), f);
	  if (bytes_in <= 0)
	    break;
	  bytes_out = fwrite(buff, 1, bytes_in, fo);
	  total_bytes += bytes_out;
	  if (bytes_in != bytes_out) {
	    do_rawlog(LT_ERR, T("Unable to write to file: %s"), outputfile);
	    fclose(f);
	    break;
	  }
	}			/* end of copy loop */
	fclose(f);
      }				/* end of being able to open file */
    }

    /* end of not being a directory */
    /* get next file matching the wildcard */
    filflag = FindNextFile(filscan, &fildata);
  } while (filflag);
  status = GetLastError();
  FindClose(filscan);
  fclose(fo);
  do_rawlog(LT_ERR, T("Copied %i %s, %ld %s"), total_files,
	    total_files == 1 ? T("file") : T("files"), total_bytes,
	    total_bytes == 1 ? T("byte") : T("bytes"));
  if (status == ERROR_NO_MORE_FILES)
    return TRUE;

  else
    return FALSE;
}

int
CheckDatabase(const char *path, FILETIME * modified, long *filesize)
{
  HANDLE filscan;
  WIN32_FIND_DATA fildata;
  SYSTEMTIME st;
  static char *months[] =
    { ">!<", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
    "Oct", "Nov", "Dec"
  };
  FILE *f;
  size_t bytes;
  filscan = FindFirstFile(path, &fildata);
  if (filscan == INVALID_HANDLE_VALUE) {
    do_rawlog(LT_ERR, "File \"%s\" not found.", path);
    return FALSE;
  }
  *modified = fildata.ftLastWriteTime;
  *filesize = fildata.nFileSizeLow;
  FindClose(filscan);
  FileTimeToSystemTime(&fildata.ftLastWriteTime, &st);
  if (st.wMonth < 1 || st.wMonth > 12)
    st.wMonth = 0;
  do_rawlog(LT_ERR, T
	    ("File \"%s\" found, size %ld %s, modified on %02d %s %04d %02d:%02d:%02d"),
	    path, fildata.nFileSizeLow,
	    fildata.nFileSizeLow == 1 ? T("byte") : T("bytes"), st.wDay,
	    months[st.wMonth], st.wYear, st.wHour, st.wMinute, st.wSecond);
  if (fildata.nFileSizeHigh == 0 && fildata.nFileSizeLow < 80) {
    do_rawlog(LT_ERR, T("File is too small to be a MUSH database."));
    return FALSE;
  }

/* check file for validity */
  f = fopen(path, "rb");
  if (!f) {
    do_rawlog(LT_ERR, T("Unable to open file %s"), path);
    return FALSE;
  }
  if (fseek(f, -80, SEEK_END) != 0) {
    do_rawlog(LT_ERR, T("Unable to check file %s"), path);
    fclose(f);
    return FALSE;
  }
  bytes = fread(buff, 1, 80, f);
  fclose(f);
  if (bytes != 80) {
    do_rawlog(LT_ERR, T("Unable to read last part of file %s"), path);
    return FALSE;
  }
  if (strstr(buff, "***END OF DUMP***") == 0) {
    do_rawlog(LT_ERR, T("Database not terminated correctly, file %s"), path);
    return FALSE;
  }
  return TRUE;
}				/* end of  CheckDatabase */

void
Win32MUSH_setup(void)
{
  int indb_OK, outdb_OK, panicdb_OK;
  FILETIME indb_time, outdb_time, panicdb_time;
  long indb_size, outdb_size, panicdb_size;

#ifndef _DEBUG
  char FileName[256];
  if (GetModuleFileName(NULL, FileName, 256) != 0) {
    if (!strcasecmp(rindex(FileName, '\\') + 1, "pennmush.exe")) {
      if (CopyFile("pennmush.exe", "pennmush_run.exe", FALSE)) {
	do_rawlog(LT_ERR, "Successfully copied executable, starting copy.");
#ifdef WIN32SERVICES
	execl("pennmush_run.exe", "pennmush_run.exe", "/run", NULL);
#else
	execl("pennmush_run.exe", "pennmush_run.exe", confname, NULL);
#endif
      }
    }
  }
#endif				/*  */
  ConcatenateFiles("txt\\hlp\\*.hlp", "txt\\help.txt");
  ConcatenateFiles("txt\\nws\\*.nws", "txt\\news.txt");
  ConcatenateFiles("txt\\evt\\*.evt", "txt\\events.txt");
  ConcatenateFiles("txt\\rul\\*.rul", "txt\\rules.txt");
  ConcatenateFiles("txt\\idx\\*.idx", "txt\\index.txt");
  indb_OK = CheckDatabase(options.input_db, &indb_time, &indb_size);
  outdb_OK = CheckDatabase(options.output_db, &outdb_time, &outdb_size);
  panicdb_OK = CheckDatabase(options.crash_db, &panicdb_time, &panicdb_size);
  if (indb_OK) {		/* Look at outdb */
    if (outdb_OK) {		/* Look at panicdb */
      if (panicdb_OK) {		/* outdb or panicdb or indb */
	if (CompareFileTime(&panicdb_time, &outdb_time) > 0) {	/* panicdb or indb */
	  if (CompareFileTime(&panicdb_time, &indb_time) > 0) {	/* panicdb */
	    ConcatenateFiles(options.crash_db, options.input_db);
	  } else {		/* indb */
	  }
	} else {		/* outdb or indb */
	  if (CompareFileTime(&outdb_time, &indb_time) > 0) {	/* outdb */
	    ConcatenateFiles(options.output_db, options.input_db);
	  } else {		/* indb */
	  }
	}
      } else {			/* outdb or indb */
	if (CompareFileTime(&outdb_time, &indb_time) > 0) {	/* outdb */
	  ConcatenateFiles(options.output_db, options.input_db);
	} else {		/* indb */
	}
      }
    } else {			/* outdb not OK */
      if (panicdb_OK) {		/* panicdb or indb */
	if (CompareFileTime(&panicdb_time, &indb_time) > 0) {	/* panicdb */
	  ConcatenateFiles(options.crash_db, options.input_db);
	} else {		/* indb */
	}
      } else {			/* indb */
      }
    }
  } else {			/* indb not OK */
    if (outdb_OK) {		/* look at panicdb */
      if (panicdb_OK) {		/* out or panic */
	if (CompareFileTime(&panicdb_time, &outdb_time) > 0) {	/* panicdb */
	  ConcatenateFiles(options.crash_db, options.input_db);
	} else {		/* outdb */
	  ConcatenateFiles(options.output_db, options.input_db);
	}
      } else {			/* outdb */
	ConcatenateFiles(options.output_db, options.input_db);
      }
    } else {			/* outdb not OK */
      if (panicdb_OK) {		/* panicdb */
	ConcatenateFiles(options.crash_db, options.input_db);
      } else {			/* NOTHING */
	return;
      }
    }
  }

/* Final failsafe - input database SHOULD still be OK. */
  do_rawlog(LT_ERR, T("Verifying selected database."));
  if (!CheckDatabase(options.input_db, &indb_time, &indb_size)) {
    do_rawlog(LT_ERR, T("File corrupted during selection process."));
    exit(-1);
  } else {
    do_rawlog(LT_ERR, T("Input database verified. Proceeding to analysis."));
  }
}

#endif				/* WIN32 */

/** Portably renames a file 
 * \param origname the orignal name of the file
 * \param newname the new name of the file
 * \return 0 on success, negative on failure
 */
int
rename_file(const char *origname, const char *newname)
{
  /* Windows can't rename over an existing file */
#ifdef WIN32
  unlink(newname);
#endif
  return rename(origname, newname);
}