/*

Record Management Services interface routines.

	This file contains a useful collection of routines
	to interface with the RMS facilities of VAX/VMS.

	alocfd	- allocate a file descriptor initialized to a default state
	closef	- close a file
	creatf	- create a new file
	delf	- delete a disk file
	genf	- generate filenames from wildcards
	getmsg	- get message text of associated status code
        getrec	- read one record
	openf	- open an existing disk file
	putrec	- write one record
	relfd	- deallocate a file descriptor
	rename	- rename a disk file
	setdir	- set default directory
	setprot	- set file protection

*/

# include <stdedc.h>		/* standard EROS C header file */
# include <rms.h>		/* Record Management Services definitions */
# include <ssdef.h>		/* standard VAX/VMS status codes */
# define DEBUG
# define	SIGNAL(status)						\
	if (status != RMS$_NORMAL)					\
		return(status);

struct	DESCRIPTOR
{
	UCOUNT	l;		/* length of object */
	UTINY	t;		/* descriptor type */
	UTINY	c;		/* descriptor class */
	TEXT	*p;		/* address of object */
};


/*			*** alocfd ***
	Allocates and initializes Record Management Services
	Record Access Block and a File Access Blocks. These blocks
	are initialized to a default state. The RAB FAB address field
	is set to point to its corresponding FAB.

	return values:
		0	- the data structure was not allocated
		nonzero	- the data structure was allocated successfullly
*/

FUNCTION struct RAB *alocfd()
{
TEXT	*calloc();
INT	cfree();
struct	FAB *fab;
struct	RAB *fd;
INT	s;

	fab = (struct FAB *) calloc(1,sizeof(struct FAB));
	if (fab == 0)
	{
#		ifdef DEBUG
			exit(SS$_ABORT);
#		else
			return(0);
#		endif
	}
	fd = (struct RAB *) calloc(1,sizeof(struct RAB));
	if (fd == 0)
	{
#		ifdef DEBUG
			printf("From alocfd: RAB not allocated\n");
			lib$stop(SS$_ABORT);
#		else
			if (cfree(fab) == 0)
				return(0);
			else
				lib$stop(SS$_ABORT);
#		endif
	}
	/* initialize RMS structures */
	*fab		= cc$rms_fab;
	*fd		= cc$rms_rab;
	fd->rab$l_fab	= fab;
	return(fd);
}

/*			*** closef ***
	Close a file.

		return values:
			standard RMS status codes
*/

FUNCTION INT closef(fd)
struct RAB *fd[];		/* address of Record Access Block pointer */
{
INT	s;
INT	sys$closef();

	s = sys$close(fd[0]->rab$l_fab);
	return(s);
}

/*			*** creatf ***
	Create a file.

		return values:
			standard RMS status codes.
*/

FUNCTION INT creatf(fn,fd,def)
struct	DESCRIPTOR *fn;		/* pointer to file name descriptor */
struct	RAB *fd[];		/* pointer to Record Access Block */
UTINY	*def;			/* 0		- don't use default access
				   nonzero	- use default access */
{
INT	s;
struct	FAB *fab;
INT	sys$connect();
INT	sys$create();

	fab = fd[0]->rab$l_fab;
	fab->fab$l_fna = fn->p;
	fab->fab$b_fns = fn->l;
	if (*def)
	{
		fab->fab$b_fac = FAB$M_PUT;	/* record write access only */
		fab->fab$l_fop = FAB$M_MXV	/* maximize version number */
			       | FAB$M_SQO;	/* sequential only access */
		fab->fab$b_rat = FAB$M_CR;	/* carraige control */
		fab->fab$b_rfm = FAB$C_VAR;	/* varaible record size */
	}
	s = sys$create(fab);
	SIGNAL(s);
	if ((fab->fab$l_fop & FAB$M_UFO) == 0)
	{
		s = sys$connect(fd[0]);
		SIGNAL(s);
	}
	return(s);
}

/*			*** delete ***
	Delete a disk file.

		return values:
			standard RMS status codes
*/

FUNCTION INT delf(fn)
struct	DESCRIPTOR *fn;		/* address of file name descriptor */
{
struct	FAB fab;
INT	s;
INT	sys$erase();

	fab = cc$rms_fab;
	fab.fab$l_fna = fn->p;
	fab.fab$b_fns = fn->l;
	s = sys$erase(&fab);
	return(s);
}
/*			*** genf ***
	Generate fully qualified file names from "wildcard" input.

		Return values:
			standard RMS status codes
*/

FUNCTION INT genf(wc,fn,fnsz,start)
struct	DESCRIPTOR *wc;	/* address of wildcard string descriptor */
struct	DESCRIPTOR *fn;	/* address of file name string descriptor */
COUNT	*fnsz;		/* length of file name string returned */
TINY	*start;		/* calling context
				1	- first call
				0	- not the first call for this set
				reset to 0 on each call */
{
static	struct FAB fab;
static	struct NAM nam;
TEXT	exstr[NAM$C_MAXRSS];
INT	s;
INT	sys$parse();
INT	sys$search();

	if (*start)
	{
		*start = 0;
		fab = cc$rms_fab;
		nam = cc$rms_nam;
		fab.fab$l_fop = FAB$M_NAM;
		fab.fab$l_fna = wc->p;
		fab.fab$b_fns = wc->l;
		fab.fab$l_nam = &nam;
		nam.nam$l_esa = exstr;
		nam.nam$b_ess = NAM$C_MAXRSS;
		s = sys$parse(&fab);
		SIGNAL(s);
	}
	nam.nam$l_rsa = fn->p;
	nam.nam$b_rss = fn->l;
	s = sys$search(&fab);
	if (s == RMS$_NMF)
		return(0);
	else if (s != RMS$_NORMAL)
	{
		TEXT	msg[73];
		struct	DESCRIPTOR msgd;
		COUNT	msgsz;
		msgd.l = 72;
		msgd.t = 0;
		msgd.c = 0;
		msgd.p = msg;
		getmsg(&fab.fab$l_stv,&msgd,&msgsz);
		msg[msgsz] = 0;
		printf("%s\n",msgd.p);
/*		printf("status = %x\n",fab.fab$l_stv);	*/
		lib$stop(s);
	}
	else
	{
		*fnsz = nam.nam$b_rsl;
		return(1);
	}
}

/*			*** getmsg ***
	Get message text of associated standard system status code.

		return values:
			standard VAX/VMS status codes.
*/

FUNCTION INT getmsg(msgid,msg,msgsz)
INT	*msgid;			/* address of status code */
struct	DESCRIPTOR *msg;	/* address of character string descriptor */
COUNT	*msgsz;			/* address of msg size to be returned */
{
INT	ls;
INT	sys$getmsg();

	ls = sys$getmsg(*msgid,msgsz,msg,0xf,0);
	return(ls);
}

/*			*** getrec ***
	Read one record from disk file.

		return values:
			standard RMS status codes
*/

FUNCTION INT getrec(fd,buf,bufsz,recsz)
struct	RAB *fd[];	/* address of RAB pointer */
TEXT	buf[];		/* address of callers read buffer */
UCOUNT	*bufsz;		/* size of callers buffer */
UCOUNT	*recsz;		/* size of record actually read */
{
INT	s;
INT	sys$get();

	fd[0]->rab$l_ubf = &buf[0];
	fd[0]->rab$w_usz = *bufsz;
	s = sys$get(fd[0]);
	SIGNAL(s);
	*recsz = fd[0]->rab$w_rsz;
	return(s);
}
/*			*** openf ***
	Open an existing disk file.

		return values:
			standard RMS status codes
*/

FUNCTION INT openf(fn,fd,maxrec,filsz,def)
struct	DESCRIPTOR *fn;		/* address of filename descriptor */
struct	RAB *fd[];		/* address of Record Access Block */
COUNT	*maxrec;		/* address of largest record in file */
INT	*filsz;			/* address of allocation quantity in K bytes */
INT	*def;			/* user file open flag */
{
struct	FAB *fab;
INT	s;
INT	sys$open();
struct	XABFHC xab;

	fab = fd[0]->rab$l_fab;
	fab->fab$l_fna = fn->p;
	fab->fab$b_fns = fn->l;
	fab->fab$l_xab = &xab;
	xab = cc$rms_xabfhc;
	s = sys$open(fab);
	SIGNAL(s);
	if (*def != 0)
	{
		fab->fab$b_shr = FAB$M_SHRGET;
	}
	if ((fab->fab$l_fop & FAB$M_UFO) == 0)
	{
		s = sys$connect(fd[0]);
		SIGNAL(s);
	}
	*maxrec	= xab.xab$w_lrl;
	*filsz	= ((fab->fab$l_alq) + 1) / 2;
	fab->fab$l_xab = 0;
	return(s);
}

/*			*** putrec ***
	Write one record to file.

		return values:
			standard RMS status codes.
*/

FUNCTION INT putrec(fd,buf,recsz)
struct	RAB *fd[];	/* address of RAB pointer */
TEXT	buf[];		/* address of callers write buffer */
UCOUNT	*recsz;		/* size of record */
{
INT	s;
INT	sys$put();

	fd[0]->rab$l_rbf = buf;
	fd[0]->rab$w_rsz = *recsz;
	s = sys$put(fd[0]);
	SIGNAL(s);
	return(s);
}

/*			*** relfd ***
	Deallocate data structure created by allocfd. This data structure
	consists of a RAB - FAB combination.

		return values:
			1		- success
			0		- failure
*/

FUNCTION INT relfd(fd)
struct	RAB *fd[];			/* pointer to RAB pointer */
{
INT	cfree();

	if (cfree(fd[0]->rab$l_fab) != 0)
#		ifdef DEBUG
			lib$stop(SS$_ABORT);
#		else
			return(0);
#		endif
	if (cfree(fd[0]) != 0)
#		ifdef DEBUG
			lib$stop(SS$_ABORT);
#		else
			return(0);
#		endif
	return(1);
}
/*			*** rename ***
	Rename a disk file.

		return values:
			standard RMS status codes
*/

FUNCTION INT renamef(oldf,newf)
struct	DESCRIPTOR *oldf;	/* address of old file name descriptor */
struct	DESCRIPTOR *newf;	/* address of new file name descriptor */
{
struct	FAB oldfd;
struct	FAB newfd;
INT	s;
INT	sys$rename();

	oldfd = cc$rms_fab;
	newfd = cc$rms_fab;
	oldfd.fab$l_fna = oldf->p;
	oldfd.fab$b_fns = oldf->l;
	newfd.fab$l_fna = newf->p;
	newfd.fab$b_fns = newf->l;
	s = sys$rename(&oldfd,&newfd);
	return(s);
}
/*			*** setdir ***
	Set default directory.
	All three parameters to this routine are optional.
		nddl = 0,	current directory stays the same
		cddl = 0,	current directory length is not returned
		cdd = 0,	current directory string is not returned

		return values:
			standard RMS status codes.
*/

FUNCTION INT setdir(ndd,cddl,cdd)
struct	DESCRIPTOR *ndd;	/* address of new default directory descriptor */
COUNT	*cddl;			/* length of current default directory */
struct	DESCRIPTOR *cdd;	/* address of descriptor to receive current default directory */
{
INT	s;
INT	sys$setddir();

	s = sys$setddir(ndd,cddl,cdd);
	return(s);
}
/*			*** setprot ***
	Set default file protection for newly created files.
	Both parameters are optional.
	If newp is 0, file protection will not be changed.
	If oldp is 0, current file protection will not be returned.

		return values:
			standard RMS status codes
*/

FUNCTION INT setprot(newp,oldp)
BITS	*newp;		/* address of new file protection mask */
BITS	*oldp;		/* address of old file protection mask */
{
INT	s;
INT	sys$setdfprot();

	s = sys$setdfprot(newp,oldp);
	return(s);
}
