/*
 * Mange set of artibrarily named data objects stored as file.
efficient storage for names files.

Each data object is referenced by a number (d) in the range 0..3932159.  The
acutal data is stored in a file with a name of the form:

    [.vxx]yy.;nn

where:
    v is volume name.
    nn = (d mod 60) + 1

    xxyy = (hex) (n/60)

Using multiple versions of filenames increases the packing efficiency of
the VMS file structure by >100%.

A bitmap file volname.bitmap manages the allocation of the bits.

A catalogue file, volname.cataloque is used to index the data objects for 
lookup and retrieval.  This file is an RMS indexed files with the following 
record structure:

    1..4		Binary, data object number.
    5..8		state
    9..16		Last access window, 2 unix times (GMT).
    17..20		Total number of opens.
    21..24		Data size.
    25..28		Modification time of object.
    29..32		reserved
    33..256		Object name, file key.

Object names must be of the form 'a:c' where a is an non-null alphanumeric 
string whose first character is non-numeric and c is a non-null alphanumeric 
string.  a name longer than 224 characters is split over multiple records and
allocates an additional object number.  The continuation record
consists of 'n:c' where n is the dummy file number.  Maximum object name
size is 4096 bytes.

The data file begins with a MIME header, using linefeeds as record terminators.
The first record is a x-catalogue: header containing a copy of the
catalogue record (expect object name is full length).

Functions:
   typedef void *volptr;
   int init_volume ( char *volume_path, char errmsg[256] );
   volptr mount(char *volume_path,  int attr_count, char **attr, 
		char errmsg[256] );
   int dismount ( volptr vol, int force_flag );
   display_volume ( vol, ctx, char *obj);

   alloc_objnum ( volptr, int *number );
   dealloc_objnum ( volptr, int number );
   number_to_fname ( volptr, number, char fname[256] );
   fname_to_number ( volptr, char *fname, int *number );

   struct object {
      volptr v;
      FILE *f;
      int number;		// catalogue name;
      char fname[256];
   }
   objptr access_catalogue ( volptr, name, create_if, header, errmsg )
   FILE *open_data ( objptr, mode );
   close_data ( objptr );
   deacess_catalogue ( objptr, delete, header, errmsg );
   fname_to_catalogue ( volptr, char *fname, char *name, errmsg );
   
 */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <descrip.h>

#include "pthread_1c_np.h"

#define BITS_PER_LONG 32
#include "cds_volume.h"
#include "cds_bitmap.h"
#include "catalogue.h"

static char *catalogue_file_fdl = "\
  FILE; ORGANIZATION indexed; PROTECTION (system:RWED,owner:RWED,group,world);\
  RECORD; CARRIAGE_CONTROL carriage_return; FORMAT variable; SIZE 256;\
  AREA 0;	BEST_TRY_CONTIGUOUS yes; BUCKET_SIZE 9; EXTENSION 512;\
  AREA 1; BEST_TRY_CONTIGUOUS yes; BUCKET_SIZE 9; EXTENSION 9;\
  AREA 2;	BEST_TRY_CONTIGUOUS yes; BUCKET_SIZE 27; EXTENSION 621;\
  KEY 0; CHANGES no; DATA_AREA 0; INDEX_AREA 1; PROLOG 3;\
          SEG0_LENGTH 4; SEG0_POSITION 0; TYPE int4;\
  KEY 1; CHANGES yes; DATA_AREA 2; INDEX_AREA 2; NULL_KEY yes;\
           SEG0_LENGTH 224; SEG0_POSITION 32; TYPE string;\
";


/***************************************************************************/
/*
 * Parse volume path into VMS directory part and name.
 */
static int parse_volume_path ( char *volume_path, 
	struct cds_volume *vol, char *errmsg )
{
    int i, unix_format;
    char *delim;
    /*
     * Split off directory and filename.
     */
    if ( strlen(volume_path) >= sizeof(vol->dir) ) {
	strcpy ( errmsg, "Illegal volume path, too long" );
	return 0;
    }
    strcpy ( vol->dir, volume_path );
    delim = vol->dir;
    for ( unix_format = i = 0; vol->dir[i]; i++ ) {
	if ( vol->dir[i] == '/' ) {
	    delim = &vol->dir[i+1];
	    unix_format = 1;
	} else if ( vol->dir[i] == ']' || vol->dir[i] == '>' ) {
	    delim = &vol->dir[i+1];
	}
    }
    if ( strlen ( delim ) >= sizeof(vol->name) ) {
	strcpy ( errmsg, "Illegal volume name, too long" );
	return 0;
    }
    strcpy ( vol->name, delim );
    *delim = '\0';
    /*
     * Convert volume directory to VMS format.
     */
    if ( unix_format ) {
	strcpy ( errmsg, "Volume path directory not in ODS-2 format" );
	return 0;
    }
    /*
     * Strip volume name of file type.
     */
    for (i=0; vol->name[i]; i++) if ( vol->name[i] == '.' ) vol->name[i] ='\0';
    return 1;
}
/*************************************************************************/
/* Create a catalogue volume.  Object limit is maximum number of catalogue
 * entries allowed, data limit is number of bytes of data.
 */
int cds_initialize ( char *volume_path, int object_limit,
	int data_limit, char errmsg[256] )
{
    int i, status;
    char fname[272];
    char bmpage[256*DENSITY/8];
    struct cds_volume template, *vol;
    struct cat_header_entry hdr;
    /*
     * Init temporary volume block and validate name.
     */
    strcpy ( errmsg, "" );
    vol = &template;
    if ( !parse_volume_path ( volume_path, vol, errmsg ) ) return 0;
    /*
     * Create the bitmap file and zero.
     */
    sprintf ( fname, "%s%s.bitmap", vol->dir, vol->name );
    vol->bitmap = fopen ( fname, "w", "MBC=32", "CTX=STM" );
    if ( !vol->bitmap ) {
	strcpy ( errmsg, "Create failure on volume bitmap file" );
	return 0;
    }
    for ( i = 0; i < sizeof(bmpage); i++ ) bmpage[i] = '\0';
    bmpage[0] = 1;		/* allocate object #0 */
    for ( i = 0; i < 256; i++ ) {
	if ( 0 >= fwrite ( bmpage, sizeof(bmpage), 1, vol->bitmap ) ) {
	    perror ( "bitmap write" );
	    strcpy ( errmsg, "Error initializing volume bitmap file" );
	    return 0;
	}
	bmpage[0] = '\0';
	if ( i*15360 >= object_limit ) break;
    }
    fclose ( vol->bitmap );
    /*
     * create the volume catalog file and write home record.
     */
    sprintf ( fname, "%s%s.catalogue", vol->dir, vol->name );
    status = ifdlcreate  ( catalogue_file_fdl, fname, "sys$disk:[]" );
    if ( status&1 == 0 ) {
	strcpy ( errmsg, "Error creating catalogue file" );
	return status;
    }

    hdr.number = hdr.data_used = hdr.size = 0;
    hdr.state = 2;
    hdr.mod_time = hdr.reserved = 0;
    hdr.data_limit = data_limit;
    hdr.object_limit = object_limit;
    for ( i =0; i <sizeof(hdr.name); i++ ) hdr.name[i] = 0;
    strcpy ( hdr.name, CATALOGUE_CONFIG_KEY );
    vol->catalogue = ifopen ( fname, "r+" );
    if ( vol->catalogue ) {
	status = ifwrite_rec ( &hdr,sizeof(hdr), vol->catalogue );
	if ( (status&1) == 0 ) {
	    sprintf ( errmsg, "Error writing catalogue record: %d", status );
	}
	ifclose ( vol->catalogue );
    } else {
	strcpy ( errmsg, "Error initializing catalogue file" );
	return 0;
    }
    return status;
}
/**********************************************************************/
/* Setup internal structures for accessing catalogue.
 */
volptr cds_mount ( char *volume_path, int attr_count,
	char **attr, char errmsg[256] )
{
    int i, status;
    size_t length;
    struct cds_volume *vol;
    struct cat_header_entry hdr;
    char fname[272];
    /*
      * Allocate volume structure and parse name.
      */
    strcpy ( errmsg, "" );
    vol = (struct cds_volume *) malloc ( sizeof(struct cds_volume) );
    if ( !vol ) {
	strcpy ( errmsg, "Memory allocation failure on mount" );
	return (void *) 0;
    }
    if ( !parse_volume_path ( volume_path, vol, errmsg ) ) {
	free ( vol );
	return (void *) 0;
    }
    for ( i = 0; i < 256; i++ ) vol->bmtable[i] = (void *) 0;
    INITIALIZE_MUTEX ( &vol->lock );
    vol->ref_count = 1;		/* us */
    vol->dismount_pending = 0;
    /*
     * Open bitmap and catalog, aborting mount on failures.
     */
    sprintf ( fname, "%s%s.BITMAP", vol->dir, vol->name );
    vol->bitmap = fopen ( fname, "rb+", "MBC=32", "CTX=STM" );
    if ( vol->bitmap ) {
	/* validate size is correct. */
    } else {
	strcpy ( errmsg, "Error openning bitmapfile" );
    }
    if ( !vol->bitmap ) {
	free ( vol );
	return (void *) 0;
    }

    sprintf ( fname, "%s%s.CATALOGUE", vol->dir, vol->name );
    vol->catalogue = ifopen ( fname, "r+" );
    if ( !vol->catalogue ) {
	strcpy ( errmsg, "Error openning catalogue file" );
	fclose ( vol->bitmap );
	free ( vol );
	return (void *) 0;
    }
    /*
     * Read header record.
     */
    for ( i =0; i <sizeof(hdr.name); i++ ) hdr.name[i] = 0;
    strcpy ( hdr.name, CATALOGUE_CONFIG_KEY );
	
    status = ifread_rec ( &hdr, sizeof(hdr), &length, vol->catalogue,
		1, hdr.name, sizeof(hdr.name) );
    if ( (status&1) == 0 ) {
	sprintf ( errmsg, "Error reading catalogue file header record: %d",
		status );
	fclose ( vol->bitmap );
	ifclose ( vol->catalogue );
	free ( vol );
	return (void *) 0;
    }
    vol->data_limit = hdr.data_limit;
    vol->object_limit = hdr.object_limit;
    vol->data_used = hdr.data_used;

    return (void *) vol;
}

/***************************************************************************/
/* Rundown mounted volume.
 */
int cds_dismount ( void *volume, int force )
{
    struct cds_volume *vol;
    int i, offset;
    /*
     * Flush bitmap to file.
     */
    cds_flush_bitmap ( (struct cds_volume *) volume, 1 );
    vol = (struct cds_volume *) volume;
    /*
     * Close files.
     */
    ifclose ( vol->catalogue );
    fclose ( vol->bitmap );
    free ( vol );
    return 1;
}
