/******************************************************************************

   FILE:  mkblkdir.c
   USAGE: mkblkdir <control file name>

          This file contains a set of routines for reconstructing a
          CODAS directory file from a set of block files.  It requires
          a control file that provides the name for the reconstructed
          CODAS database, the name of the producer definition file to
          be used, optionally the name(s) of structure definition file(s)
          that define user-defined structures in the database in case
          these were not loaded with the block file, and the names of
          the block files that will be included in this database.
          See the file mkblkdir.cnt for an example.

                            Eric Firing
                             88-01-16

 *****************************************************************************/
#include "dbinc.h"
#include "ioserv.h"
#include "mc.h"

#if PROTOTYPE_ALLOWED
void do_it(FILE *fp_cnt);
int do_loadsd(FILE *fp, char *arg, int code);
int do_add_block_file(FILE *fp, char *arg, int code);
char *add_block_file(FILE_NAME_TYPE fn_old_block);
void dbcreate();
#else
void do_it();
int do_loadsd();
int do_add_block_file();
char *add_block_file();
void dbcreate();
#endif

static FILE_NAME_TYPE db_name, prd_name;
static DATASET_ID_TYPE dataset_id;
static char dest[40], block_dir_type[2];
static int DestMachine;

static OPTION_TYPE options2[] =
{
   "end"        , 0, NULL             , (char *) NULL,
   "SD_NAME"    , 0, do_loadsd        , (char *) NULL,
   "BLOCK_FILE" , 0, do_add_block_file, (char *) NULL,
   NULL         , 0, NULL             , (char *) NULL
};

static OPTION_TYPE options1[] =
{
   "end"        , 0          , NULL        , (char *) NULL,
   "DB_NAME"    , TYPE_STRING, op_get_param, db_name,
   "PRD_NAME"   , TYPE_STRING, op_get_param, prd_name,
   "DATASET_ID" , TYPE_STRING, op_get_param, (char *) &dataset_id,
   "BLOCK_DIR_TYPE", TYPE_STRING, op_get_param, block_dir_type,
   "DESTINATION", TYPE_STRING, op_get_param, dest,       
   NULL         , 0          , NULL        , (char *) NULL
};

NAME_LIST_ENTRY_TYPE hosts[] =
{
   "PC_COMPATIBLE_HOST"  , 0,
   "VAXD_COMPATIBLE_HOST", 1,
   "SUN3_COMPATIBLE_HOST", 2,
   NULL                  , -1
};

void do_it(fp_cnt)      
FILE *fp_cnt;
{
   int db_id = 1;
   int im = 0;
   int ier;

#if (COMPILER == TURBO_C)
#include <math.h>
   sin(0.0);
#endif

   /* initialize options to defaults */
   strcpy(db_name, "");
   strcpy(prd_name, "");
   strcpy(dataset_id.instrument_name, "");
   strcpy(block_dir_type, "");
   DestMachine = HOST_ENVIRONMENT;

   if (execute_options(fp_cnt, options1, ECHO) < 0) 
      return;

   fflush(stdout);
   if (!strcmp(db_name, ""))
   {
      fprintf(stderr, "\n ERROR:  Missing DB_NAME in control file\n\n");
      return;
   }
   if (strcmp(prd_name, "") && (strcmp(dataset_id.instrument_name, "") ||
				  strcmp(block_dir_type, "")) ) 
   {  /* can't have both PRD_NAME and DATASET_ID, etc. in control file */
      fprintf(stderr, "\n ERROR:  A PRD_NAME or\n");
      fprintf(stderr, "\n         DATASET_ID, PRODUCER_ID and BLOCK_DIR_TYPE must be specified.\n\n");
      return;
   }
   if ( (!strcmp(prd_name, "") && (!strcmp(dataset_id.instrument_name, "") ||
				   !strcmp(block_dir_type, "")) ))
   {  /* if both PRD_NAME and DATASET_ID, etc. not given, set up some defaults */
      strcpy(dataset_id.instrument_name, "ADCP-VM");
      strcpy(block_dir_type, "0");
   }

   if (strcmp(dest, "") && (DestMachine = get_code(hosts, dest)) == BADINT)
   {
      fprintf(stderr, "\n ERROR:  Unrecognized destination %s\n\n", dest);
      return;
   }

   dbcreate(&db_id, db_name, prd_name, &im, &ier);
   if (DBERROR(&ier, "in DBCREATE")) return;

   execute_options(fp_cnt, options2, ECHO);

   DBCLOSE(&ier);
   if (DBERROR(&ier, "in DBCLOSE")) return;
   if (DestMachine != HOST_ENVIRONMENT)
      convert_block_dir(db_name, DestMachine);
   puts("\n");
}

#if PROTOTYPE_ALLOWED
int do_loadsd(FILE *fp, char *arg, int code)
#else
int do_loadsd(fp, arg, code)
FILE *fp;
char *arg;
int code;
#endif
{
   int ier = 0;
   FILE_NAME_TYPE sd_name;

   if (!getword_nc(fp, sd_name, sizeof(FILE_NAME_TYPE)))
   {
      printf("\n ERROR: Scanning control file for structure definition file name\n");
      return(-1);
   }
   DBLOADSD(sd_name, &ier);
   if (DBERROR(&ier, "in DBLOADSD")) return(-1);
   return(0);
}
   
#if PROTOTYPE_ALLOWED
int do_add_block_file(FILE *fp, char *arg, int code)
#else
int do_add_block_file(fp, arg, code)
FILE *fp;
char *arg;
int code;
#endif
{
   FILE_NAME_TYPE fn_old_block; 
   char *fn_new_block;

   if (!getword_nc(fp, fn_old_block, sizeof(FILE_NAME_TYPE)))
   {
      printf("\n ERROR: Scanning control file for block file name\n");
      return(-1);
   }
   fn_new_block = add_block_file(fn_old_block);
   if (fn_new_block == NULL)
   {
      printf("\n ERROR: Adding block file = %s", fn_old_block);
      return(-1);
   }
   printf(" %s ---> %s", fn_old_block, fn_new_block);
   return(0);
}

/*-----------------------------------------------------------------------------

   FUNCTION: copy_file

      This does a rather slow byte-by-byte binary file copy.

   ARGUMENTS:

      fn_from     source file name
      fn_to       destination file name

   RETURNS:  0    if successful,
             EOF  otherwise.

*/
#if PROTOTYPE_ALLOWED
int copy_file(char *fn_from, char *fn_to)
#else
int copy_file(fn_from, fn_to)
char *fn_from, *fn_to;
#endif
{
   FILE *fp_from, *fp_to;
   char c;

   if ( (fp_from = fopen(fn_from, "rb")) == NULL ) return EOF;
   if ( (fp_to = fopen(fn_to, "wb")) == NULL ) return EOF;

#ifdef VBUF
#define VBUFSIZE 8192
   if (setvbuf(fp_from, NULL, _IOFBF, VBUFSIZE) ||
       setvbuf(fp_to  , NULL, _IOFBF, VBUFSIZE))
   {
      printf("\n ERROR: Cannot set up buffers for file copy\n\n");
      return(EOF);
   }
#endif

   c = fgetc(fp_from);
   while (!feof(fp_from))
   {
      fputc(c, fp_to);
      c = fgetc(fp_from);
   }
   fclose(fp_to);
   fclose(fp_from);
   return(0);
}

/*----------------------------------------------------------

   FUNCTION: add_block_file

      This is for adding an existing block file to a database
      opened with DBCREATE.  The intended applications are the
      construction of new working databases from a conglomeration
      of data blocks, the reconstruction of lost or damaged
      block directories, and the construction of block directories
      with data blocks that have been transferred and need to be
      translated from one machine to another.

   ARGUMENT: file name of the block file to be added.

   RETURNS:  pointer to the name of the new block file, if successful;
             NULL otherwise.

*/
#if PROTOTYPE_ALLOWED
char *add_block_file(FILE_NAME_TYPE fn_old_block)
#else
char *add_block_file(fn_old_block)
FILE_NAME_TYPE fn_old_block;
#endif
{
   FILE *fp_new_block;
   int from;
/*
 ---> Set up a block directory entry and set the file ID.
*/
   create_block_dir_entry();
/*
  ---> SET NAME OF BLOCK FILE
*/
   sprintf( (db->block_file+db->path_nchar),
      db->block_dir_hdr.block_file_template,
      db->block_dir_entry_ptr->file_id);

   if (!EXISTS(db->block_file))
   {
      if ( (db->error_code = convert_block_file(fn_old_block, db->block_file,
         DestMachine)) != 0 ) /* jr */
      {
         printf("\n ERROR: Copying %s to %s", fn_old_block, db->block_file);
         goto error_found;
      }
   }
   else
   {
      if (stricmp(fn_old_block, db->block_file) != 0)
      /*
       ---> If a file with the new name exists, but this name is not the
            same as that of the current old block file, then there is a
            danger that the old file names may be out of sequence, or for
            some other reason we may be in danger of accidentally
            overwriting a block file.  The file name comparison is done
            without case sensitivity.
            Fri  09-08-1995      */
      {
         printf("\n ERROR in add_block_file: New file name duplicates");
         printf("\n       an old one, but the files do not correspond.\n");
         goto error_found;
      }
      else
      {
         if ( (fp_new_block = fopen(db->block_file, "rb")) == NULL )
         {
            printf("\n ERROR: Can't open %s\n", db->block_file);
            goto error_found;
         }
         fread(&(db->block_hdr), BLOCK_HDR_SIZE, 1, fp_new_block);
         fclose(fp_new_block);
         if ((from = identify_ms_code(db->block_hdr.ms_code)) == UNKNOWN_HOST)
         {
            db->error_code = UNKNOWN_HOST_ERROR;
            db->error_data = BLOCK_HDR;
            goto error_found;
         }
         if (from != DestMachine)
         {
            printf("\n ERROR in add_block_file: In-place platform");
            printf("\n       conversion not allowed at this time.\n");
            goto error_found;
         }
      }
   }
   update_block_dir_entry();
   if (add_block_dir_entry())
      goto error_found;
   db->new_block_is_open = 0;
   db->block_dir_modified = 1;
   return(db->block_file);

   error_found:
      report_db_error("add_block_file");
      return(NULL);
}


/*-----------------------------------------------------------------------------

   FUNCTION:  dbcreate

      It opens a new or existing database for appending.
      This version is modified from the dbsource/dbcreate.c version
      in that it does not have to load the producer definition from an
      external file; instead it just reads the 3 items it needs
      from the user's control file:  DATASET_ID, PRODUCER_ID and
      BLOCK_DIR_TYPE.

   PARAMETERS:
   
      db_id = pointer to database number

      db_name = pointer to database name, including path;
                replaced by name without path.

      prd_name = pointer to name of producer definition file

      memory_mode = pointer to memory mode for directories

      ierr = pointer to error code

   RETURNS:  VOID

*/
#if PROTOTYPE_ALLOWED
void dbcreate(int *db_id, char *db_name, char *prd_name, int *memory_mode, int *ierr)
#else
void dbcreate(db_id, db_name, prd_name, memory_mode, ierr)
int *db_id, *memory_mode, *ierr;
char *db_name, *prd_name;
#endif
{
   int i, id;
   FILE_NAME_TYPE path_name, path, db_root;          /* 80 characters each */
   char answ[2];

   *ierr = 0;
   if ((*ierr = alignment_check()) != 0)
      return;  /**jr++**/
   if (first_database_call)
   {
      for (i = 0;  i < MAX_OPEN_DATABASES;  i++)
         database_table[i] = NULL;
      first_database_call = 0;
   }
   id = *db_id - 1;
   if ((id < 0) || (id >= MAX_OPEN_DATABASES))
   {
      *ierr = INVALID_DATABASE_NUMBER;
      return;
   }
   if (database_table[id])
   {
      db = database_table[id];
      *ierr = DB_ALREADY_OPEN;
      return;
   }
   if ((database_table[id] = (DATABASE_TYPE *) malloc(DATABASE_SIZE)) == NULL)
   {
      db->error_code = INSUFFICIENT_MEMORY;
      db->error_data = BLOCK_DIR;
      goto error_found;
   }

   current_database = id;
   db = database_table[id];
   set_byte((UBYTE *)db, 0, DATABASE_SIZE);
   if (*memory_mode)
      db->memory_directory = 1;
   else
      db->memory_directory = 0;

   strtrim(path_name, db_name, sizeof(FILE_NAME_TYPE));
   if (strlen(path_name) + 8 > sizeof(FILE_NAME_TYPE))
   {                         /* need 8 char for file number, ext, NUL */
      *ierr = PATHNAME_TOO_LONG;
      return;
   }
   split_path(path_name, path, db_root);
   if (strlen(db_root) > MAX_DBNAME_LENGTH)
   {
      *ierr = DBNAME_TOO_LONG;
      return;
   }
   db->path_nchar = strlen(path);

   strcpy(db->block_dir_hdr.block_file_template, db_root);
   strcat(db->block_dir_hdr.block_file_template, "%03u.blk");

   strcpy(db->block_file, path);
   strcpy(db->block_dir_file, path);
   strcat(db->block_dir_file, db_root);
   strcat(db->block_dir_file, "dir.blk");

/*
  ---> ALLOCATE MEMORY FOR PRODUCER DEFINITION
*/
   if ((db->producer = (PRODUCER_DEFINITION_TYPE *)
      malloc(sizeof(PRODUCER_DEFINITION_TYPE))) == NULL)
   {
      db->error_code = INSUFFICIENT_MEMORY;
      db->error_data = PRODUCER_DEF;
      goto error_found;
   }
   db->producer_in_memory = 1;
   set_byte((UBYTE *)db->producer, '\0', sizeof(PRODUCER_DEFINITION_TYPE));
   if (strcmp(prd_name, ""))
   {
     strtrim(db->producer_def_file, prd_name, sizeof(FILE_NAME_TYPE));
     tmpnam(db->temporary_data_file);
     if (load_producer_definition())
       goto error_found;
   }
   else
   {
     strncpy((char *) &db->producer->dataset_id, (char *) &dataset_id, sizeof(DATASET_ID_TYPE));
     db->producer->block_dir_type = atol(block_dir_type);
   }

   db->access_mode = READ_WRITE;
   db->create_mode = 1;
   if (EXISTS(db->block_dir_file))
   {
      fprintf(stdout, "\n ERROR: File %s already exists!  Overwrite? <y|n>", db->block_dir_file);
      scanf(" %2s", answ);
      if (answ[0] == 'n') 
      {
         db->error_code = FILE_PROTECTION_ERROR;
         db->error_data = BLOCK_DIR_FILE;
         goto error_found;
      }
   }
   if (create_block_dir())
      goto error_found;
   if (open_block_dir())
      goto error_found;
   if (db->memory_directory)
   {
      if (allocate_memory_for_directories())
         goto error_found;
      if (db->block_dir_hdr.dir_nentries)
      {
         if (load_block_dir())
            goto error_found;
      }
   }
   db->block_dir_loaded = 1;
   
   return;

   error_found:
      report_db_error("dbcreate");
      *ierr = db->error_code * 1000 - db->error_data;
      release_database();
}

