/*
**
** Copyright © 1992, TGV, Incorporated
**
** RMS-based file I/O routines.  Not susceptible to VAXCRTL's UNIX-like
** treatment of LF's and NUL's.
**
*/
#include <stdlib.h>
#include <fab.h>
#include <rab.h>
#include <nam.h>
#include <xab.h>
#include <ssdef.h>
#include <stsdef.h>
#include <rmsdef.h>

struct UNIT {
    struct FAB fab;
    struct RAB rab;
    struct NAM nam;
    struct XABFHC xabfhc;
    char espec[255], rspec[255];
};

#define CLOSE_AND_CLEANUP(u) {\
    	SYS$CLOSE(&(u->fab),0,0);\
    	u->fab.fab$b_dns = 0;\
    	u->nam.nam$l_rlf = 0;\
    	u->nam.nam$b_nop |= NAM$M_SYNCHK;\
    	SYS$PARSE(&(u->fab),0,0);\
    	}

/*
**++
**  ROUTINE:	file_create
**
**  FUNCTIONAL DESCRIPTION:
**
**  	Creates a new file, open for writing.
**
**  RETURNS:	cond_value, longword (unsigned), write only, by value
**
**  PROTOTYPE:
**
**  	file_create (fspec, unit)
**
**  IMPLICIT INPUTS:	None.
**
**  IMPLICIT OUTPUTS:	None.
**
**  COMPLETION CODES:
**
**
**  SIDE EFFECTS:   	None.
**
**--
*/
unsigned int file_create(char *fspec, struct UNIT **upp) {

    struct UNIT *u;
    unsigned int status;

    u = malloc(sizeof(struct UNIT));
    if (!u) return SS$_INSFMEM;
    u->fab = cc$rms_fab;
    u->rab = cc$rms_rab;
    u->nam = cc$rms_nam;
    u->fab.fab$l_fna = fspec;
    u->fab.fab$b_fns = strlen(fspec);
    u->fab.fab$l_fop = FAB$M_SQO | FAB$M_DFW;
    u->fab.fab$b_fac = FAB$M_PUT|FAB$M_TRN;
    u->fab.fab$b_rat = FAB$M_CR;
    u->fab.fab$b_rfm = FAB$C_VAR;
    u->fab.fab$w_mrs = 0;
    u->nam.nam$l_esa = u->espec;
    u->nam.nam$b_ess = sizeof(u->espec);
    u->nam.nam$l_rsa = u->rspec;
    u->nam.nam$b_rss = sizeof(u->rspec);
    u->fab.fab$l_nam = &(u->nam);
    u->rab.rab$l_fab = &(u->fab);
    u->rab.rab$l_rop |= RAB$M_WBH;
    status = SYS$CREATE(&(u->fab),0,0);
    if ($VMS_STATUS_SUCCESS(status)) {
    	status = SYS$CONNECT(&(u->rab),0,0);
    	if ($VMS_STATUS_SUCCESS(status)) {
    	    *upp = u;
    	    return status;
    	}
    	u->fab.fab$l_fop |= FAB$M_DLT;
    	CLOSE_AND_CLEANUP(u);
    }
    free(u);
    return status;

} /* file_create */

/*
**++
**  ROUTINE:	file_open
**
**  FUNCTIONAL DESCRIPTION:
**
**  	Opens an existing file for reading.
**
**  RETURNS:	cond_value, longword (unsigned), write only, by value
**
**  PROTOTYPE:
**
**  	file_open(fspec, unit, [defspec])
**
**  IMPLICIT INPUTS:	None.
**
**  IMPLICIT OUTPUTS:	None.
**
**  COMPLETION CODES:
**
**
**  SIDE EFFECTS:   	None.
**
**--
*/
unsigned int file_open(char *fspec, struct UNIT **upp, char *defspec) {

    struct UNIT *u;
    unsigned int status;

    u = malloc(sizeof(struct UNIT));
    if (!u) return SS$_INSFMEM;
    u->fab = cc$rms_fab;
    u->rab = cc$rms_rab;
    u->nam = cc$rms_nam;
    u->xabfhc = cc$rms_xabfhc;
    u->fab.fab$l_fna = fspec;
    u->fab.fab$b_fns = strlen(fspec);
    if (defspec) {
    	u->fab.fab$l_dna = defspec;
    	u->fab.fab$b_dns = strlen(defspec);
    }
    u->fab.fab$b_fac = FAB$M_GET;
    u->fab.fab$l_xab = (void *) &(u->xabfhc);
    u->nam.nam$l_esa = u->espec;
    u->nam.nam$b_ess = sizeof(u->espec);
    u->nam.nam$l_rsa = u->rspec;
    u->nam.nam$b_rss = sizeof(u->rspec);
    u->fab.fab$l_nam = &(u->nam);
    u->rab.rab$l_fab = &(u->fab);
    u->rab.rab$l_rop |= RAB$M_RAH;
    status = SYS$OPEN(&(u->fab),0,0);
    if ($VMS_STATUS_SUCCESS(status)) {
    	status = SYS$CONNECT(&(u->rab),0,0);
    	if ($VMS_STATUS_SUCCESS(status)) {
    	    u->rab.rab$w_usz = u->fab.fab$w_mrs == 0 ?
    	    	    (u->xabfhc.xab$w_lrl == 0 ? 32767 : u->xabfhc.xab$w_lrl) :
    	    	    u->fab.fab$w_mrs;
    	    u->rab.rab$l_ubf = malloc(u->rab.rab$w_usz);
    	    if (u->rab.rab$l_ubf) {
    	    	*upp = u;
    	    	return status;
    	    } else status = SS$_INSFMEM;
    	    SYS$DISCONNECT(&(u->rab),0,0);
    	}
    	CLOSE_AND_CLEANUP(u);
    }
    free(u);
    return status;

} /* file_open */

/*
**++
**  ROUTINE:	file_append
**
**  FUNCTIONAL DESCRIPTION:
**
**  	Opens an existing file for reading.
**
**  RETURNS:	cond_value, longword (unsigned), write only, by value
**
**  PROTOTYPE:
**
**  	file_append(fspec, unit, [defspec])
**
**  IMPLICIT INPUTS:	None.
**
**  IMPLICIT OUTPUTS:	None.
**
**  COMPLETION CODES:
**
**
**  SIDE EFFECTS:   	None.
**
**--
*/
unsigned int file_append(char *fspec, struct UNIT **upp, char *defspec) {

    struct UNIT *u;
    unsigned int status;

    u = malloc(sizeof(struct UNIT));
    if (!u) return SS$_INSFMEM;
    u->fab = cc$rms_fab;
    u->rab = cc$rms_rab;
    u->nam = cc$rms_nam;
    u->xabfhc = cc$rms_xabfhc;
    u->fab.fab$l_fna = fspec;
    u->fab.fab$b_fns = strlen(fspec);
    u->fab.fab$b_org = FAB$C_SEQ;
    u->fab.fab$b_rfm = FAB$C_STMLF;
    u->fab.fab$b_rat = 0;
    if (defspec) {
    	u->fab.fab$l_dna = defspec;
    	u->fab.fab$b_dns = strlen(defspec);
    }
    u->fab.fab$b_fac = FAB$M_PUT|FAB$M_TRN;
    u->fab.fab$b_shr = FAB$M_UPI|FAB$M_GET;
    u->fab.fab$l_fop = FAB$M_CIF;
    u->fab.fab$l_xab = (void *) &(u->xabfhc);
    u->nam.nam$l_esa = u->espec;
    u->nam.nam$b_ess = sizeof(u->espec);
    u->nam.nam$l_rsa = u->rspec;
    u->nam.nam$b_rss = sizeof(u->rspec);
    u->fab.fab$l_nam = &(u->nam);
    u->rab.rab$l_fab = &(u->fab);
    u->rab.rab$l_rop |= RAB$M_RAH | RAB$M_EOF;
    status = SYS$CREATE(&(u->fab),0,0);
    if ($VMS_STATUS_SUCCESS(status)) {
    	int created;
    	created = status == RMS$_CREATED;
    	status = SYS$CONNECT(&(u->rab),0,0);
    	u->rab.rab$l_rop &= ~RAB$M_EOF;
    	if ($VMS_STATUS_SUCCESS(status)) {
    	    *upp = u;
    	    return status;
    	}
    	if (created) u->fab.fab$l_fop |= FAB$M_DLT;
    	CLOSE_AND_CLEANUP(u);
    }
    free(u);
    return status;

} /* file_append */

/*
**++
**  ROUTINE:	file_fidopen
**
**  FUNCTIONAL DESCRIPTION:
**
**  	Opens an existing file by fileid for reading.
**
**  RETURNS:	cond_value, longword (unsigned), write only, by value
**
**  PROTOTYPE:
**
**  	file_fidopen (fileid, unit, actual_filename, actual_filename_size)
**
**  IMPLICIT INPUTS:	None.
**
**  IMPLICIT OUTPUTS:	None.
**
**  COMPLETION CODES:
**
**
**  SIDE EFFECTS:   	None.
**
**--
*/
unsigned int file_fidopen(char *fileid, struct UNIT **upp) {

    struct UNIT *u;
    unsigned int status;

    u = malloc(sizeof(struct UNIT));
    if (!u) return SS$_INSFMEM;
    u->fab = cc$rms_fab;
    u->rab = cc$rms_rab;
    u->nam = cc$rms_nam;
    u->xabfhc = cc$rms_xabfhc;
    u->fab.fab$b_fac = FAB$M_GET;
    u->fab.fab$l_xab = (void *) &(u->xabfhc);
    memcpy(&(u->nam.nam$t_dvi), fileid, 28);
    u->fab.fab$l_fop |= FAB$M_NAM;
    u->fab.fab$l_nam = &(u->nam);
    u->rab.rab$l_fab = &(u->fab);
    u->rab.rab$l_rop |= RAB$M_RAH;
    status = SYS$OPEN(&(u->fab),0,0);
    if ($VMS_STATUS_SUCCESS(status)) {
    	status = SYS$CONNECT(&(u->rab),0,0);
    	if ($VMS_STATUS_SUCCESS(status)) {
    	    u->rab.rab$w_usz = u->fab.fab$w_mrs == 0 ?
    	    	    (u->xabfhc.xab$w_lrl == 0 ? 32767 : u->xabfhc.xab$w_lrl) :
    	    	    u->fab.fab$w_mrs;
    	    u->rab.rab$l_ubf = malloc(u->rab.rab$w_usz);
    	    if (u->rab.rab$l_ubf) {
    	    	*upp = u;
    	    	return status;
    	    } else status = SS$_INSFMEM;
    	    SYS$DISCONNECT(&(u->rab),0,0);
    	}
    	SYS$CLOSE(&(u->fab),0,0);
    }
    free(u);
    return status;

} /* file_fidopen */

/*
**++
**  ROUTINE:	file_close
**
**  FUNCTIONAL DESCRIPTION:
**
**  	Closes a file opened by file_open or file_create.
**
**  RETURNS:	cond_value, longword (unsigned), write only, by value
**
**  PROTOTYPE:
**
**  	tbs
**
**  IMPLICIT INPUTS:	None.
**
**  IMPLICIT OUTPUTS:	None.
**
**  COMPLETION CODES:
**
**
**  SIDE EFFECTS:   	None.
**
**--
*/
unsigned int file_close(struct UNIT *u) {

    unsigned int status;

    if (u->rab.rab$w_usz > 0) free(u->rab.rab$l_ubf);
    SYS$DISCONNECT(&(u->rab),0,0);
    status = SYS$CLOSE(&(u->fab),0,0);
    if ($VMS_STATUS_SUCCESS(status)) {
    	u->fab.fab$b_dns = 0;
    	u->nam.nam$l_rlf = 0;
    	u->nam.nam$b_nop |= NAM$M_SYNCHK;
    	SYS$PARSE(&(u->fab),0,0);
    }
    free(u);
    return status;

}

/*
**++
**  ROUTINE:	file_dclose
**
**  FUNCTIONAL DESCRIPTION:
**
**  	Closes & deletes a file currently open for writing.
**
**  RETURNS:	cond_value, longword (unsigned), write only, by value
**
**  PROTOTYPE:
**
**  	tbs
**
**  IMPLICIT INPUTS:	None.
**
**  IMPLICIT OUTPUTS:	None.
**
**  COMPLETION CODES:
**
**
**  SIDE EFFECTS:   	None.
**
**--
*/
unsigned int file_dclose(struct UNIT *u) {

    unsigned int status;

    if (!(u->fab.fab$b_fac & FAB$M_PUT)) return SS$_BADPARAM;

    if (u->rab.rab$w_usz > 0) free(u->rab.rab$l_ubf);
    SYS$DISCONNECT(&(u->rab),0,0);
    u->fab.fab$l_fop |= FAB$M_DLT;
    status = SYS$CLOSE(&(u->fab),0,0);
    if ($VMS_STATUS_SUCCESS(status)) {
    	u->fab.fab$b_dns = 0;
    	u->nam.nam$l_rlf = 0;
    	u->nam.nam$b_nop |= NAM$M_SYNCHK;
    	SYS$PARSE(&(u->fab),0,0);
    }
    free(u);
    return status;

}

/*
**++
**  ROUTINE:	file_read
**
**  FUNCTIONAL DESCRIPTION:
**
**  	Reads a record from a file.
**
**  RETURNS:	cond_value, longword (unsigned), write only, by value
**
**  PROTOTYPE:
**
**  	tbs
**
**  IMPLICIT INPUTS:	None.
**
**  IMPLICIT OUTPUTS:	None.
**
**  COMPLETION CODES:
**
**
**  SIDE EFFECTS:   	None.
**
**--
*/
unsigned int file_read(struct UNIT *u, char *buf, int buf_size, int *retlen) {

    unsigned int status;

    status = SYS$GET(&(u->rab),0,0);
    u->rab.rab$b_rac = RAB$C_SEQ;
    if ($VMS_STATUS_SUCCESS(status)) {
    	*retlen = u->rab.rab$w_rsz > buf_size ? buf_size : u->rab.rab$w_rsz;
    	memcpy(buf, u->rab.rab$l_rbf, *retlen);
    }
    return status;
}

/*
**++
**  ROUTINE:	file_write
**
**  FUNCTIONAL DESCRIPTION:
**
**  	Writes a record to a file.
**
**  RETURNS:	cond_value, longword (unsigned), write only, by value
**
**  PROTOTYPE:
**
**  	tbs
**
**  IMPLICIT INPUTS:	None.
**
**  IMPLICIT OUTPUTS:	None.
**
**  COMPLETION CODES:
**
**
**  SIDE EFFECTS:   	None.
**
**--
*/
unsigned int file_write(struct UNIT *u, char *buf, int buf_size) {

    if (u->rab.rab$b_rac == RAB$C_RFA) {
    	sys$truncate(&u->rab,0,0);
    }
    u->rab.rab$b_rac = RAB$C_SEQ;
    u->rab.rab$l_rbf = buf;
    u->rab.rab$w_rsz = buf_size;
    return SYS$PUT(&(u->rab),0,0);

}

/*
**++
**  ROUTINE:	file_getpos
**
**  FUNCTIONAL DESCRIPTION:
**
**  	Returns the RFA of the next available record in the file.
**
**  RETURNS:	cond_value, longword (unsigned), write only, by value
**
**  PROTOTYPE:
**
**  	tbs
**
**  IMPLICIT INPUTS:	None.
**
**  IMPLICIT OUTPUTS:	None.
**
**  COMPLETION CODES:
**
**
**  SIDE EFFECTS:   	None.
**
**--
*/
unsigned int file_getpos(struct UNIT *u, short rfa[3]) {

    unsigned int status;

    status = SYS$FIND(&(u->rab),0,0);
    if (status == RMS$_EOF && (u->fab.fab$b_fac & FAB$M_PUT)) {
    	u->rab.rab$l_rbf = "";
    	u->rab.rab$w_rsz = 0;
    	status = SYS$PUT(&(u->rab),0,0);
    }
    if ($VMS_STATUS_SUCCESS(status))
    	memcpy(rfa, &(u->rab.rab$w_rfa), 6);
    u->rab.rab$b_rac = RAB$C_RFA;
    return status;

} /* file_getpos */

/*
**++
**  ROUTINE:	file_setpos
**
**  FUNCTIONAL DESCRIPTION:
**
**  	Moves the file to the specified RFA.
**
**  RETURNS:	cond_value, longword (unsigned), write only, by value
**
**  PROTOTYPE:
**
**  	tbs
**
**  IMPLICIT INPUTS:	None.
**
**  IMPLICIT OUTPUTS:	None.
**
**  COMPLETION CODES:
**
**
**  SIDE EFFECTS:   	None.
**
**--
*/
unsigned int file_setpos(struct UNIT *u, short rfa[3]) {

    unsigned int status;

    if (rfa == (short *) 0) return SYS$REWIND(&(u->rab),0,0);
    if (rfa[0] == 0 && rfa[1] == 0 && rfa[2] == 0)
    	return SYS$REWIND(&(u->rab),0,0);

    u->rab.rab$b_rac = RAB$C_RFA;
    memcpy(&(u->rab.rab$w_rfa), rfa, 6);
    status = SYS$FIND(&(u->rab),0,0);
    return status;

} /* file_setpos */

/*
**++
**  ROUTINE:	file_parse
**
**  FUNCTIONAL DESCRIPTION:
**
**  	Interface to the $PARSE RMS service.  Can return entire file
**  specification or just the device & directory portion.
**
**  RETURNS:	cond_value, longword (unsigned), write only, by value
**
**  PROTOTYPE:
**
**  	file_parse(char *fspec, char *defspec, char *result_spec, int dir_only)
**
**  IMPLICIT INPUTS:	None.
**
**  IMPLICIT OUTPUTS:	None.
**
**  COMPLETION CODES:
**
**
**  SIDE EFFECTS:   	None.
**
**--
*/
unsigned int file_parse(char *fspec, char *defspec, char *result_spec,
    	    	    	    int dir_only) {

    struct FAB fab;
    struct NAM nam;
    char espec[255], rspec[255];
    unsigned int status;

    fab = cc$rms_fab;
    fab.fab$l_nam = &nam;
    fab.fab$l_fna = fspec;
    fab.fab$b_fns = strlen(fspec);
    if (defspec) {
    	fab.fab$l_dna = defspec;
    	fab.fab$b_dns = strlen(defspec);
    }
    nam = cc$rms_nam;
    nam.nam$l_esa = espec;
    nam.nam$b_ess = sizeof(espec);
    nam.nam$l_rsa = rspec;
    nam.nam$b_rss = sizeof(rspec);
    status = SYS$PARSE(&fab,0,0);
    if ($VMS_STATUS_SUCCESS(status)) {
    	if (*nam.nam$l_dir == '<') {	/* change <dir> to [dir] */
    	    *nam.nam$l_dir = '[';
    	    *(nam.nam$l_dir+nam.nam$b_dir-1) = ']';
    	}
    	if (dir_only) {
    	    int len;
    	    len = nam.nam$b_esl-nam.nam$b_ver-nam.nam$b_type-nam.nam$b_name;
    	    strncpy(result_spec, espec, len);
    	    *(result_spec+len) = '\0';
    	} else {
    	    strncpy(result_spec, espec, nam.nam$b_esl);
    	    *(result_spec+nam.nam$b_esl) = '\0';
    	}
    }

    fab.fab$b_dns = 0;
    nam.nam$l_rlf = 0;
    nam.nam$b_nop |= NAM$M_SYNCHK;
    SYS$PARSE(&fab,0,0);

    return status;

}

/*
**++
**  ROUTINE:	file_truncate
**
**  FUNCTIONAL DESCRIPTION:
**
**  	tbs
**
**  RETURNS:	cond_value, longword (unsigned), write only, by value
**
**  PROTOTYPE:
**
**  	tbs
**
**  IMPLICIT INPUTS:	None.
**
**  IMPLICIT OUTPUTS:	None.
**
**  COMPLETION CODES:
**
**
**  SIDE EFFECTS:   	None.
**
**--
*/
unsigned int file_truncate(struct UNIT *unit) {

    return sys$truncate(&unit->rab, 0, 0);

} /* file_truncate */

/*
**++
**  ROUTINE:	file_get_rdt
**
**  FUNCTIONAL DESCRIPTION:
**
**  	Fetches the RDT for a file.
**
**  RETURNS:	cond_value, longword (unsigned), write only, by value
**
**  PROTOTYPE:
**
**  	file_get_rdt(fspec, rdt)
**
**  IMPLICIT INPUTS:	None.
**
**  IMPLICIT OUTPUTS:	None.
**
**  COMPLETION CODES:
**
**
**  SIDE EFFECTS:   	None.
**
**--
*/
unsigned int file_get_rdt(char *fspec, unsigned int rdt[2]) {

    struct FAB fab;
    struct XABRDT xabrdt;
    unsigned int status;

    fab = cc$rms_fab;
    xabrdt = cc$rms_xabrdt;
    fab.fab$l_fna = fspec;
    fab.fab$b_fns = strlen(fspec);
    fab.fab$b_fac = FAB$M_GET;
    fab.fab$b_shr = FAB$M_SHRPUT|FAB$M_SHRGET;
    fab.fab$l_xab = (char *) &xabrdt;
    status = SYS$OPEN(&fab,0,0);
    if ($VMS_STATUS_SUCCESS(status)) {
    	memcpy(rdt, &xabrdt.xab$q_rdt, 8);
    	SYS$CLOSE(&fab,0,0);

    } else {
    	rdt[0] = rdt[1] = 0;
    }

    return status;

} /* file_get_rdt */
