/* $Id: initdb.c,v 1.15 2002/03/10 21:17:52 richdawe Exp $ */

/*
 *  initdb.c - Database initialisation routines for zippo
 *  Copyright (C) 1999-2002 by Richard Dawe
 *      
 *  This program 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 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "common.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>

/* libzippo includes */
#include <libzippo/package.h>
#include <libzippo/packlist.h>
#include <libzippo/util.h>

#include "zippo.h"
#include "rcfile.h"
#include "platform.h"
#include "initdb.h"

/* Default DSMs for different platforms */
extern char *platform_dsm_table[];
extern char *platform_dsm_name_table[];

extern char zipporc_default[];     /* Default zipporc configuration file */
extern char simtelrc_default[];    /* Default simtelrc configuration file */
extern char zippo_dsm[];           /* DSM for zippo */
extern char zippo_dsm_filename[];  /* Filename for DSM for zippo */

typedef struct {
  platform_t platform;
  /* int major_version;
     int minor_version; */
  char dsm_name[PACKAGE_MAXNAMELEN]; /* DSM name for this platform */
  char name[PACKAGE_MAXNAMELEN];     /* More readable name */
} platform_mapping_t;

platform_mapping_t platform_mapping_table[]={
  { PLATFORM_NONE,   "none",   "None"         },
  { PLATFORM_MSDOS,  "msdos",  "MS-DOS"       },
  { PLATFORM_WIN3,   "win3",   "Windows 3"    },
  { PLATFORM_WIN95,  "win95",  "Windows '95"  },
  { PLATFORM_WIN98,  "win98",  "Windows '98"  },
  { PLATFORM_WINME,  "winme",  "Windows ME"   },
  { PLATFORM_WINNT4, "winnt4", "Windows NT 4" },
  { PLATFORM_WIN2K,  "win2k",  "Windows 2000" },
  { PLATFORM_WINXP,  "winxp",  "Windows XP"   },
  { PLATFORM_DOSEMU, "dosemu", "dosemu"       },
  { 0,               "",       ""             }
};	  

/* ------------------
 * - perform_initdb -
 * ------------------ */

int
perform_initdb (const ZIPPO_INITDB *req)
{
  FILE *fp = NULL;
  char db_path[PATH_MAX], db_avail_path[PATH_MAX], share_path[PATH_MAX];
  char path[PATH_MAX];
  int i, j, found, ret;
  mode_t mode;

  /* for the new os detection and adding of the platform to the
   * req->platforms array */
  platform_t   detected_platform;
  char        *detected_platform_dsm_name = NULL;
  char        *detected_platform_name     = NULL;
  char       **platforms = NULL;
  int          detected_platform_already_specified = 0;
  int          n_platforms;

  /* Construct share, db & db-avail paths. */
  strcpy(share_path, req->root);
  addforwardslash(share_path);
  strcat(share_path, ZIPPO_SHARE_PREFIX);
  addforwardslash(share_path);

  strcpy(db_path, req->root);
  addforwardslash(db_path);
  strcat(db_path, ZIPPO_DB_PREFIX);
  addforwardslash(db_path);

  strcpy(db_avail_path, req->root);
  addforwardslash(db_avail_path);
  strcat(db_avail_path, ZIPPO_DB_AVAIL_PREFIX);
  addforwardslash(db_avail_path);

  /* Create the root & share/zippo/db directories */
  strcpy(path, share_path);
  printf(". Creating database structure in '%s'...", path);

  mode = S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH;
  ret =               recursive_mkdir(path, mode);
  ret = (ret == 0) && recursive_mkdir(db_path, mode);
  ret = (ret == 0) && recursive_mkdir(db_avail_path, mode);

  if ((ret != 0) && (errno != EEXIST)) {
    printf("\n");
    perror("zippo");
    return(EXIT_FAILURE);
  }

  printf("done\n");

  /* Platform detection code added by Kalum (thanks!). */
  detected_platform = detect_platform();

  /* Display the OS detected */
  detected_platform_name = platform_mapping_table[detected_platform].name;

  detected_platform_dsm_name
    = platform_mapping_table[detected_platform].dsm_name;

  printf(". Detected platform: %s\n", detected_platform_name);

  /*
   * Make a copy of the current platform list and the detected platform
   * to it.
   */
  for (n_platforms = i = 0, detected_platform_already_specified = 0;
       (req->platforms != NULL) && (req->platforms[i] != NULL);
       n_platforms++, i++) {
    /* find whether the detected platform has already been
     * specified by the user */
    if (strcmp(req->platforms[i], detected_platform_dsm_name) == 0) {
      /* No need to do anything - set 'already specified' flag, so
       * that the platform is not added twice to the platform list. */
      detected_platform_already_specified = 1;
    }     
  }

  if (!detected_platform_already_specified)
    n_platforms++;

  platforms = malloc(sizeof(char *) * (n_platforms + 1));
  if (platforms == NULL) {
    warnf("Unable to allocate memory - %s:%d", __FILE__, __LINE__);
    return(EXIT_FAILURE);
  }

  /* Copy exisiting platform list. */
#define FREE_STRINGS(x) \
  if (x != NULL) { \
    for (i = 0; (x)[i] != NULL; i++) { \
      free((x)[i]); \
    } \
    free(x); \
  }

  for (i = 0; (req->platforms != NULL) && (req->platforms[i] != NULL); i++) {
    platforms[i] = strdup(req->platforms[i]);
    if (platforms[i] == NULL) {
      FREE_STRINGS(platforms);

      warnf("Unable to allocate memory - %s:%d", __FILE__, __LINE__);
      return(EXIT_FAILURE);
    }
  }

  if (!detected_platform_already_specified) {
    platforms[i] = strdup(detected_platform_dsm_name);
    if (platforms[i] == NULL) {
      FREE_STRINGS(platforms);

      warnf("Unable to allocate memory - %s:%d", __FILE__, __LINE__);
      return(EXIT_FAILURE);
    }

    platforms[i + 1] = NULL;
  } else {
    platforms[i] = NULL;
  }

#undef FREE_STRINGS

  /* Create any platform DSMs necessary. */
  if (platforms != NULL)
    for (i = 0; platforms[i] != NULL; i++) {
      /* Find the DSM name in the table. */
      for (found = -1, j = 0; platform_dsm_name_table[j] != NULL; j++) {
	if (strcmp(platforms[i], platform_dsm_name_table[j]) == 0) {
	  found = j;
	  break;
	}
      }

      if (found == -1) {
	warnf("Unable to find built-in DSM for platform '%s'!", platforms[i]);
	continue;
      }

      /* Create a DSM file. */
      strcpy(path, db_path);
      strcat(path, platforms[i]);
      strcat(path, ".dsm");
      printf(". Creating plaform DSM '%s'...", path);

      fp = fopen(path, "wt");
      if (fp == NULL) {
	printf("\n");
	warnf("Unable to write DSM for platform '%s'!", platforms[i]);
	continue;
      }
      fputs(platform_dsm_table[found], fp);
      fclose(fp);
      printf("done\n");
    }

  /* - Create a default zipporc, if necessary. - */

  /* Check for a configuration file. */
  strcpy(path, share_path);
  strcat(path, RCFILE_DEFAULT);

  if (access(path, R_OK) != 0) {
    /* Need to create default configuration file. */
    printf(". Creating default configuration file '%s'...",
	   path);

    fp = fopen(path, "wt");
    if (fp == NULL) {
      printf("\n");
      /* TODO: Should this tidy up first? */
      dief("Unable to create configuration file '%s'!\n", path);
    }

    fputs(zipporc_default, fp);
    fclose(fp);
    printf("done\n");
  } else {
    infof("Configuration file '%s' found & kept!", path);
  }

  /* - Create a default simtelrc, if necessary. - */

  /* Check for a configuration file. */
  strcpy(path, share_path);
  strcat(path, "simtelrc"); /* TODO: Put this in a constant somewhere. */

  if (access(path, R_OK) != 0) {
    /* Need to create default configuration file. */
    printf(". Creating default configuration file '%s'...", path);

    fp = fopen(path, "wt");
    if (fp == NULL) {
      printf("\n");
      /* TODO: Should this tidy up first? */
      dief("Unable to create configuration file '%s'!\n", path);
    }

    fputs(simtelrc_default, fp);
    fclose(fp);
    printf("done\n");
  } else {
    infof("Configuration file '%s' found & kept!", path);
  }

  /* Copy the zippo binary and DSM, if necessary. */
  if (req->zippo != NULL) {
    /* Copy the binary */
    strcpy(path, req->root);
    strcat(path, "bin/");
	
    ret = recursive_mkdir(path, mode);

    if ((ret != 0) && (errno != EEXIST)) {
      warnf("Unable to create program directory '%s'", path);
      perror("zippo");
      return(EXIT_FAILURE);
    }

#ifdef __DJGPP__
    strcat(path, "zippo.exe");
#else
    strcat(path, "zippo");
#endif

    if (strcmp(req->zippo, path) == 0) {
      printf(". Cannot install zippo binary '%s' onto itself - skipping\n",
	     path);
    } else {
      if (!copy_file(req->zippo, path)) {
	warnf("Unable to copy zippo binary '%s' -> '%s'", req->zippo, path);
	return(EXIT_FAILURE);
      } else {
	printf(". Copied zippo binary: '%s' -> '%s'\n",
	       req->zippo, path);
      }
    }

    /* Install the DSM */
    strcpy(path, db_path);
    strcat(path, zippo_dsm_filename);

    printf(". Installing zippo's DSM '%s'...", path);

    fp = fopen(path, "wt");
    if (fp == NULL) {
      printf("\n");
      /* TODO: Should this tidy up first? */
      dief("Unable to create zippo's DSM '%s'!\n", path);
    }

    fputs(zippo_dsm, fp);
    fclose(fp);
    printf("done\n");
  }

  return(EXIT_SUCCESS);
}
