/*
 *	Copyright (C) 1984, 1985  SRI International
 *	Copyright (C) 1988, 1989, 1992	TGV, Incorporated
 *
 *	System dependent code
 *
 */
#include <errno.h>
#include <timeb.h>
#include <time.h>
#include <ctype.h>
#include <stat.h>
#include "uafdef.h"
#include <jpidef.h>
#include <rmsdef.h>
#include <prvdef.h>
#include <lnmdef.h>
#include <fab.h>
#include <rab.h>
#include <xabdef.h>
#include <nam.h>
#include <descrip.h>
#include <ssdef.h>
#include <lckdef.h>
#include <fibdef.h>
#include <dvidef.h>
#include <iodef.h>
#include <uaidef.h>
#include <psldef.h>
#include "date.h"
#include <stdio.h>
#include <stdlib.h>
#include "vax-mm.h"

static struct UAF uaf;
static int uaf_valid;
static int Get_Uaf_Record();
static get_record();
static char Home_Directory[128];
static int get_exec_logical(char *, char *, int, int);

/*
 *	Get our Hostname
 */
char *Get_Hostname()
{
	static char Hostname[32];

	if (This_Hosts_Name[0]) return(This_Hosts_Name);
	if (Hostname[0]) return(Hostname);
    	if (get_exec_logical("MULTINET_SMTP_TO_HOST",
    	    	Hostname, sizeof(Hostname), 0)) return Hostname;
	if (!VMS$TrnLNM("MULTINET_SMTP_HOST_NAME", Hostname, sizeof(Hostname)))
		gethostname(Hostname,sizeof(Hostname));
	if (Hostname[0] == 0) return("");
	return(Hostname);
}

char *Get_From_Hostname()
{
    static char from_host[256];
    if (get_exec_logical("MULTINET_SMTP_FROM_HOST",
    	    	from_host, sizeof(from_host), 0)) return from_host;
    return Get_Hostname();

}
char *Get_Hostname_Only()
{
	static char Hostname[32];

	if (This_Hosts_Name[0]) return(This_Hosts_Name);
	if (Hostname[0]) return(Hostname);
	gethostname(Hostname,sizeof(Hostname));
	if (Hostname[0] == 0) return("");
	return(Hostname);
}



/*
 *	Get our Username
 */
char *Get_Username()
{
	static char Username[sizeof(uaf.uaf$t_username)+2];
	register char *cp,*cp1;
	register int i;

	/*
	 *	Get the UAF record
	 */
	if (!Get_Uaf_Record()) return("");
	/*
	 *	Copy the username
	 */
	cp = (char *)uaf.uaf$t_username;
	cp1 = Username;
	for(i = 0; i < sizeof(uaf.uaf$t_username); i++) {
		if (*cp == ' ') break;
		*cp1++ = *cp++;
	}
	*cp1 = 0;
	/*
	 *	Done
	 */
	return(Username);
}


/*
 *	Get our home directory
 */
char *Get_Home_Directory()
{
	char *cp;

	if (Home_Directory[0]) return(Home_Directory);
	/*
	 *	Try for SYS$LOGIN:
	 */
	if ((cp = (char *)getenv("SYS$LOGIN")) != 0) {
		strcpy(Home_Directory,cp);
		return(Home_Directory);
	}
	/*
	 *	Try for HOME environment variable
	 */
	if ((cp = (char *)getenv("HOME")) != 0) {
		strcpy(Home_Directory,cp);
		strcat(Home_Directory,"/");
		return(Home_Directory);
	}
	/*
	 *	Try the UAF record
	 */
	if (Get_Uaf_Record()) {
		strncpy(Home_Directory,
			uaf.uaf$t_defdev+1,
			uaf.uaf$t_defdev[0]);
		strncpy(Home_Directory+uaf.uaf$t_defdev[0],
			uaf.uaf$t_defdir+1,
			uaf.uaf$t_defdir[0]);
		Home_Directory[uaf.uaf$t_defdev[0]+uaf.uaf$t_defdir[0]] = 0;
		return(Home_Directory);
	}
	/*
	 *	Try getpwuid()
	 */
	/*
	 *	LOSE!!!
	 */
	printf("?Couldn't find your home directory\n");
	return("");
}


/*
 *	Get our personal name (if possible)
 */
char *Get_Personal_Name()
{
	register char *cp,*cp1;
	int isalf=0,wasalf=0,i;

	/*
	 *	If we already have the person name, return it
	 */
	if (Personal_Name[0] != 0) return(Personal_Name);

	/*
	 *	Get the UAF record
	 */
	if (!Get_Uaf_Record()) return("");
	/*
	 *	Get the personal name
	 */
	cp = (char *)uaf.uaf$t_owner+1;
	i = uaf.uaf$t_owner[0];
	cp[i] = 0;
	/*
	 *	Delete trailing blanks
	 */
	cp1 = cp + i;
	while(cp1 > cp) {
		cp1--;
		if ((*cp1 != ' ') && (*cp1 != 0)) break;
		*cp1 = 0;
	}

	/*
	 *	Tastefully capitalize the personal name and stop at ","
	 */
	cp1 = Personal_Name;
	while(*cp) {
		if (*cp == ',') break;
		isalf = isalpha(*cp);
		*cp1++ = wasalf&isalf&isupper(*cp) ? tolower(*cp++) : *cp++;
		wasalf = isalf;
	}
	*cp1 = 0;
	return(Personal_Name);
}


/*
 *	Get our UAF record
 */
static int Get_Uaf_Record()
{
	struct {
		unsigned short int Size;
		unsigned short int Type;
		char *Buffer;
		int *Resultant_Size;
		} JPI_Item_List[2],UAI_Item_List[5];
	struct {int Size; char *Ptr;} Descr;
	char Username[32+4];
	int Size = 0;
	struct FAB fab;
	struct RAB rab;
	unsigned int old_privs[2],new_privs[2];
	int status;

	/*
	 *	If already valid, return success
	 */
	if (uaf_valid) return(1);
	/*
	 *	Otherwise, do the lookup (1st get our user name)
	 */
	JPI_Item_List[0].Size = 32;
	JPI_Item_List[0].Type = JPI$_USERNAME;
	JPI_Item_List[0].Buffer = Username;
	JPI_Item_List[0].Resultant_Size = &Size;

	JPI_Item_List[1].Size = 0;  JPI_Item_List[1].Type = 0;

	sys$getjpi(0,0,0,JPI_Item_List,0,0,0);
#ifndef NO_GETUAI
	/*
	 *	Try using $GETUAI to get the UAF information
	 */
	UAI_Item_List[0].Size = sizeof(uaf.uaf$t_username);
	UAI_Item_List[0].Type = UAI$_USERNAME;
	UAI_Item_List[0].Buffer = (char *) uaf.uaf$t_username;
	UAI_Item_List[0].Resultant_Size = 0;

	UAI_Item_List[1].Size = sizeof(uaf.uaf$t_defdev);
	UAI_Item_List[1].Type = UAI$_DEFDEV;
	UAI_Item_List[1].Buffer = (char *) uaf.uaf$t_defdev;
	UAI_Item_List[1].Resultant_Size = 0;

	UAI_Item_List[2].Size = sizeof(uaf.uaf$t_defdir);
	UAI_Item_List[2].Type = UAI$_DEFDIR;
	UAI_Item_List[2].Buffer = (char *) uaf.uaf$t_defdir;
	UAI_Item_List[2].Resultant_Size = 0;

	UAI_Item_List[3].Size = sizeof(uaf.uaf$t_owner);
	UAI_Item_List[3].Type = UAI$_OWNER;
	UAI_Item_List[3].Buffer = (char *) uaf.uaf$t_owner;
	UAI_Item_List[3].Resultant_Size = 0;

	UAI_Item_List[4].Size = 0; UAI_Item_List[4].Type = 0;

	Descr.Ptr = Username;
	Descr.Size = Size;

	status = sys$getuai(0,0,&Descr,UAI_Item_List,0,0,0);
	if (status & 1) {
		/*
		 *	Got it the quick and easy way!!
		 */
		uaf_valid = 1;
		return(1);
	}
#endif	NO_GETUAI
	/*
	 *	Didn't get it, try reading SYSUAF.DAT...
	 *		Zero the fab and rab
	 */
	bzero(&fab,sizeof(fab));
	bzero(&rab,sizeof(rab));
	/*
	 *	Setup the fab
	 */
	fab.fab$b_bid = FAB$C_BID;
	fab.fab$b_bln = sizeof(fab);
	fab.fab$l_fna = "SYSUAF";
	fab.fab$b_fns = 6;
	fab.fab$l_dna = "SYS$SYSTEM:.DAT";
	fab.fab$b_dns = 15;
	fab.fab$b_fac |= FAB$M_GET;
	fab.fab$b_shr = (FAB$M_GET|FAB$M_PUT|FAB$M_UPD|FAB$M_DEL);
	fab.fab$b_org = FAB$C_IDX;
	fab.fab$b_rfm = FAB$C_VAR;
	/*
	 *	setup the rab
	 */
	rab.rab$b_bid = RAB$C_BID;
	rab.rab$b_bln = sizeof(rab);
	rab.rab$b_rac = RAB$C_KEY;
	rab.rab$l_rop |= RAB$M_NLK;
	rab.rab$b_mbc = 2;
	rab.rab$w_usz = sizeof(struct UAF);
	rab.rab$l_ubf = (char *)&uaf;
	rab.rab$l_kbf = (char *)Username;
	rab.rab$b_ksz = Size;
	rab.rab$l_fab = &fab;
	/*
	 *	Enable all privileges that we are authorized to have just to
	 *	enhance the possibility of accessing the SYSUAF file.
	 */
	new_privs[0] = -1; new_privs[1] = -1;
	sys$setprv(1,new_privs,0,old_privs);
	/*
	 *  Open the File and connect the RAB
	 */
	status = sys$open(&fab);
	if (!(status & 1)) {
		if ((status == RMS$_SNE) ||
		    (status == RMS$_SPE) ||
		    (status == RMS$_DME)) {
			fab.fab$b_shr = 0;
			status = sys$open(&fab);
			if (!(status & 1)) goto exit;
		} else goto exit;
	}
	status = sys$connect(&rab);
	if (!(status & 1)) goto exit;
	/*
	 *	Look up the User's UAF record
	 */
	status = get_record(&rab);
	if (status == RMS$_RNF) status = 0;

exit:
	/*
	 *	Done: close the file, release privileges and return status
	 */
	sys$disconnect(&rab);
	sys$close(&fab);
	sys$setprv(0,new_privs,0,0);
	sys$setprv(1,old_privs,0,0);
	if (!(status & 1)) return(0);
	uaf_valid = 1;
	return(1);
}


/*
 *	Read the record and deal with file locking
 */
static get_record(rab)
struct RAB *rab;
{
	static int wakedelta[] = {-10*1000*75,-1};
	int retries = 2;
	int status;

	/*
	 *	Re-try the appropriate number of times
	 */
	while(1) {
		/*
		 *	Get the record
		 */
		status = sys$get(rab);
		/*
		 *	If the return code is not "Record Locked" it is either
		 *	a success or error that we can't handle, return it.
		 */
		if (status != RMS$_RLK) break;
		/*
		 *	Record Locked:	If retries exceeded, return error
		 */
		if (--retries < 0) break;
		/*
		 *	Retry: Sleep first
		 */
		status = sys$schdwk(0,0,wakedelta,0);
		if (status & 1) sys$hiber();
	}
	/*
	 *	Done: Return status
	 */
	return(status);
}


/*
 *	Routine to read into memory the text of a message not yet read in
 */
char *Read_In_Message(Message_Number)
{
	char *Return_Value;
	register int i,j;
	register int Offset;
	register int Current_Offset;
	int File_Offset;

	/*
	 *	If already in memory, just return its address
	 */
	if ((Return_Value = Messages[Message_Number-1].Header) != 0)
							return(Return_Value);
	/*
	 *	Get it off the disk
	 */
	Offset = 0;
	for(i = 1; i < Message_Number; i++)
			Offset += Messages[i-1].Real_Size;
	lseek(Text_File_FD,Offset,0);
	read(Text_File_FD,Message_Text+Offset,Messages[i-1].Real_Size);
	Messages[i-1].Header = Message_Text+Offset;
	return(Messages[Message_Number-1].Header);
}


/*
 *	Return our current timezone
 */
char *Timezone_String(RFC822_Timezone)
int RFC822_Timezone;
{
	char **p;
	register char *cp, *cp1;
	int Now_GMT[2], Now_Localtime[2];
	char Temp[64];
	static char Timezone[64];
	int GMT_Offset_Minutes;
	int GMT_Offset_Hours;
	int GMT_Offset_Direction;

	/*
	 *	Get the timezone (there are 6 characters to ignore)
	 */
	odtim_jsys(0,ODT_DO_TIMEZONE|ODT_NO_SECONDS|ODT_NO_DATE,Temp);
	/*
	 *	If we are not doing RFC822 timezones, we can just use
	 *	what we got from ODTIM_JSYS
	 */
	if (!RFC822_Timezone) {
		strcpy(Timezone, Temp+6);
		return(Timezone);
	}
	/*
	 *	Figure out a GMT+-nnnn timezone name
	 */
	idtim_jsys("now", 0, Now_GMT);
	gmt_to_localtime(Now_GMT, Now_Localtime, 0);
	GMT_Offset_Minutes = Now_Localtime[0] - Now_GMT[0];
	if (GMT_Offset_Minutes == 0) return("GMT");
	if (GMT_Offset_Minutes >= 0) {
		GMT_Offset_Direction = 0;
	} else {
		GMT_Offset_Direction = 1;
		GMT_Offset_Minutes = -GMT_Offset_Minutes;
	}
	GMT_Offset_Minutes /= 60;
	GMT_Offset_Hours = GMT_Offset_Minutes / 60;
	GMT_Offset_Minutes -= (GMT_Offset_Hours * 60);
	sprintf(Timezone,
		"%c%02d%02d",
		GMT_Offset_Direction ? '-' : '+',
		GMT_Offset_Hours,
		GMT_Offset_Minutes);
	/*
	 *	Don't allow UT[+-nnnn] or GMT[+-nnnn] formats
	 */
	cp = Temp+6;
	if (((cp[0] != 'U') ||
	     (cp[1] != 'T') ||
	     ((cp[2] != '-') && (cp[2] != '+'))) &&
	    ((cp[0] != 'G') ||
	     (cp[1] != 'M') ||
	     (cp[2] != 'T') ||
	     ((cp[3] != '-') && (cp[3] != '+')))) {
	    sprintf(Timezone+strlen(Timezone)," (%s)", cp);
	}
	return(Timezone);
}


/*
 *	eXtended open (accounts for possibility of running SETUID)
 */
int Xopen(Filename,Flags)
char *Filename;
int Flags;
{

	return(open(Filename, Flags, 0, "mbc=32"));
}


/*
 *	eXtended fopen (accounts for possibility of running SETUID)
 */
FILE *Xfopen(Filename,Flags)
char *Filename;
char *Flags;
{
	return(fopen(Filename, Flags, "mbc=32"));
}


/*
 *	Extended creat (accounts for SETUID and VMS text files)
 */
int Xcreat(Filename,Mode,Text_File)
char *Filename;
int Mode;
char *Text_File;
{
	int i, j;
	static int SysPriv[2] = {PRV$M_SYSPRV, 0};
	int Old_Privs[2];

	/*
	 *	Turn off SYSPRV
	 */
	Old_Privs[0] = 0; Old_Privs[1] = 0;
	i = sys$setprv(0,SysPriv,0,Old_Privs);
	/*
	 *	Create the file
	 */
	j = umask(0);
	if (Text_File == 0) {
			i = creat(Filename, Mode, "mbc=32");
	} else {
		i = creat(Filename,Mode,"rfm=var","rat=cr");
	}
	j = umask(j);
	/*
	 *	Turn on SYSPRV (if it was on)
	 */
	if (Old_Privs[0] & PRV$M_SYSPRV) sys$setprv(1,SysPriv,0,0);
	/*
	 *	Return creat status
	 */
	return(i);
}


/*
 *	eXtended access (accounts for SETUID)
 */
int Xaccess(Filename,Mode)
char *Filename;
int Mode;
{
	int Status;
	struct FAB Fab;

	/*
	 *	VMS: Talk directly to RMS
	 */
	bzero(&Fab, sizeof(Fab));
	Fab.fab$b_bid = FAB$C_BID;
	Fab.fab$b_bln = sizeof(Fab);
	Fab.fab$b_fac = FAB$M_GET;
	if (Mode & 2) Fab.fab$b_fac |= FAB$M_PUT;
	Fab.fab$l_fna = Filename;
	Fab.fab$b_fns = strlen(Filename);
	Status = sys$open(&Fab);
	if (!(Status & 1)) return(-1);
	sys$close(&Fab);
	return(1);
}




/*
 *	Get the time of our last login (internal format)
 */
int Last_Login()
{
	struct {
		unsigned short int Size;
		unsigned short int Type;
		char *Buffer;
		int *Resultant_Size;
		} JPI_Item_List[2];
	int BinTime[2];
	struct {int Size; char *Ptr;} Descr;
	char Local[32];

	/*
	 *	Get the login time (VMS binary format)
	 */
	JPI_Item_List[0].Size = sizeof(BinTime);
	JPI_Item_List[0].Type = JPI$_LOGINTIM;
	JPI_Item_List[0].Buffer = (char *)BinTime;
	JPI_Item_List[0].Resultant_Size = 0;
	JPI_Item_List[1].Size = 0;  JPI_Item_List[1].Type = 0;

	sys$getjpi(0,0,0,JPI_Item_List,0,0,0);
	/*
	 *	Change it to ASCII format (DD-MMM-YYYY HH:MM:SS)
	 */
	Descr.Ptr = Local;
	Descr.Size = 20;
	sys$asctim(0,&Descr,BinTime,0);
	Local[20] = 0;
	/*
	 *	Now convert to internal time
	 */
	idtim_jsys(Local,0,BinTime);
	/*
	 *	Return the internal time
	 */
	return(BinTime[0]);
}


/*
 *	link()/unlink()/rename()/read() for VAX-11 "C"
 */
static char Rename_To_File[256];
int MM_unlink(File)
char *File;
{
	int i;

	if (Rename_To_File[0] != 0) {
		i = Vax11_C_rename(File,Rename_To_File);
		Rename_To_File[0] = 0;
		return(i);
	}
	return(delete(File));
}

link(From_File,To_File)
char *From_File;
char *To_File;
{
	strcpy(Rename_To_File,To_File);
	return(0);
}

int Vax11_C_rename(From,To)
char *From;
char *To;
{
	struct FAB From_Fab;
	struct FAB To_Fab;
	int i;

	/*
	 *	Setup FABS
	 */
	bzero(&From_Fab,sizeof(From_Fab));
	From_Fab.fab$b_bid = FAB$C_BID;
	From_Fab.fab$b_bln = sizeof(From_Fab);
	From_Fab.fab$l_fna = From;
	From_Fab.fab$b_fns = strlen(From);
	bzero(&To_Fab,sizeof(To_Fab));
	To_Fab.fab$b_bid = FAB$C_BID;
	To_Fab.fab$b_bln = sizeof(To_Fab);
	To_Fab.fab$l_fna = To;
	To_Fab.fab$b_fns = strlen(To);
	/*
	 *	Do the rename
	 */
	i = sys$rename(&From_Fab,0,0,&To_Fab);
	/*
	 *	Check for errors
	 */
	if (!(i & 1)) {
		extern noshare int vmserrno;

		vmserrno = i;
		return(-1);
	}
	/*
	 *	Success
	 */
	return(0);
}

/*
 *	A read() for VAX-11 "C" that doesn't care about "size"
 */
int vax11c_read(fd,buf,size)
char *buf;
{
#undef	read
	int i,j;

	i = 0;
	while(i < size) {
		j = size - i;
		if (j > 1024) j = 1024;
		j = read(fd,buf+i,j);
		if (j <= 0) {
			if (j < 0) return(j);
			break;
		}
		i += j;
	}
	return(i);
}

/*
 *	A write() for VAX-11 "C" that doesn't care about "size"
 */
int vax11c_write(fd,buf,size)
char *buf;
{
#undef	write
	/*
	 *	Vax-11 "C" is brain damaged
	 */
#define MAX_WRITE	(32*1024)		/* Max = 32Kb writes */
	int Total = 0;
	int i;

	while(size > 0) {
		/*
		 *	Write a chunk
		 */
		i = write(fd,buf,(size > MAX_WRITE) ? MAX_WRITE : size);
		if (i < 0) return(i);
		/*
		 *	Account for it
		 */
		Total += i;
		size -= i;
		buf += i;
	}
	return(Total);
}

/*
 *	Open a file with shared access (for VAX-11 "C")
 */
int Vax11_C_Shared_Open(Filename,Flags)
char *Filename;
int Flags;
{
	char *cp;

	return(open(Filename,Flags,0,"shr=get","shr=put","shr=upd","mbc=32"));
}



/*
 *	Determine which mail delivery system to use
 */
Mail_Delivery_System()
{
	char *cp;

	/*
	 *	Default is VMS mail
	 */
	VMS_Mail_Delivery_System = VMSMAIL_DELIVERY;
	/*
	 *	See if the mail system is explicitly specified
	 */
	cp = (char *)getenv("MM_MAIL_DELIVERY_SYSTEM");
	if (cp) {
		/*
		 *	Yes:  See which one
		 */
		if (strcasecmp(cp,"VMS-MAIL") == 0) {
			VMS_Mail_Delivery_System = VMSMAIL_DELIVERY;
			goto Done;
		}
#ifdef	MULTINET
		if (strcasecmp(cp,"MULTINET-MAIL") == 0) {
			VMS_Mail_Delivery_System = MULTINET_DELIVERY;
			goto Done;
		}
#endif	MULTINET
#ifdef	PMDF
		if (strcasecmp(cp,"PMDF") == 0) {
			VMS_Mail_Delivery_System = PMDF_DELIVERY;
			goto Done;
		}
#endif	PMDF
#ifdef MX
    	    	if (strcasecmp(cp,"MX") == 0) {
    	    	    	VMS_Mail_Delivery_System = MX_DELIVERY;
    	    	    	goto Done;
    	    	}
#endif
		/*
		 *	Don't understand the specification
		 */
		printf("WARNING:  Don't understand mail delivery system \"%s\"\n",cp);
	}
#ifdef	PMDF
	cp = (char *)getenv("PMDF_SHARE_LIBRARY");
	if (cp) {
		VMS_Mail_Delivery_System = PMDF_DELIVERY;
		goto Done;
	}
#endif	PMDF
#ifdef  MX
    	cp = (char *)getenv("MX_SHR");
    	if (cp) {
    	    	VMS_Mail_Delivery_System = MX_DELIVERY;
    	    	goto Done;
    	}
#endif MX
#ifdef	MULTINET
	cp = (char *)getenv("MULTINET_SMTP_QUEUE");
	if (cp) {
		VMS_Mail_Delivery_System = MULTINET_DELIVERY;
		goto Done;
	}
#endif	MULTINET
Done:
	switch (VMS_Mail_Delivery_System) {
	    case VMSMAIL_DELIVERY:
	    case STMAIL_DELIVERY:
	    case MULTINET_DELIVERY:
	    case PMDF_DELIVERY:
    	    case MX_DELIVERY:
		Incorporate_Newmail_File = 1;
		break;
	}
}


/*
 *	Try to take out a lock to prevent ourselves from simultaneously
 *	running MM from another process (and hence screwing up MAIL.TXT.
 */

int Create_MM_Lock()
{
	char lock_name[64];
	register int status;
	long lksb[4];
	struct dsc$descriptor resnam;

	strcpy(lock_name,"MM_");
	strcat(lock_name,(char *)Get_Username());
	resnam.dsc$w_length  = strlen(lock_name);
	resnam.dsc$a_pointer = lock_name;
	resnam.dsc$b_class   = DSC$K_CLASS_S;
	resnam.dsc$b_dtype   = DSC$K_DTYPE_T;

	enable_image_privilege();
	status = SYS$ENQ(0,LCK$K_EXMODE,lksb,LCK$M_NOQUEUE|LCK$M_SYSTEM,
			 &resnam,0,0,0,0,0,0);
	disable_image_privilege();
	if (status == SS$_NOTQUEUED) {
		return(0);
	} else if (!(status&1)) {
		(void) LIB$SIGNAL(status);
	} else if (!lksb[0]) {
		return(0);
	} else {
		return(1);
	}
}



/*
 *	VMS dependent error translation routine
 */
#include <descrip.h>
#include <errno.h>
#include <stdio.h>

char *getvaxerr(vaxerr)
unsigned long vaxerr;
{
	static char buf[256];
	short len;
	struct dsc$descriptor dsc;

	dsc.dsc$w_length  = sizeof(buf) - 1;
	dsc.dsc$a_pointer = buf;
	dsc.dsc$b_class   = DSC$K_CLASS_S;
	dsc.dsc$b_dtype   = DSC$K_DTYPE_T;

	(void) SYS$GETMSG(vaxerr,&len,&dsc,0x0f,0);
	buf[len] = '\0';
	return(buf);
}

static void Extract_Device_Name(char *fspec, char *devnam, int namsize) {

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

    fab = cc$rms_fab;
    fab.fab$l_fna = fspec;
    fab.fab$b_fns = strlen(fspec);
    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);
    nam.nam$b_nop |= NAM$M_NOCONCEAL;
    fab.fab$l_nam = (void *) &nam;
    status = sys$parse(&fab,0,0);
    if (status & 1) {
    	len = nam.nam$b_dev;
    	if (len > namsize-1) len = namsize-1;
    	bcopy(nam.nam$l_dev, devnam, len);
    } else {
    	len = 0;
    }
    devnam[len] = '\0';
    return;

}

int Free_Disk_Space(char *fspec) {

    char devnam[256];
    struct dsc$descriptor dsc;
    static unsigned int code = DVI$_FREEBLOCKS;
    unsigned int status;
    int blocks;

    Extract_Device_Name(fspec, devnam, sizeof(devnam));
    if (devnam[0] == '\0') return 0;

    dsc.dsc$a_pointer = devnam;
    dsc.dsc$w_length = strlen(devnam);
    dsc.dsc$b_dtype = DSC$K_DTYPE_T;
    dsc.dsc$b_class = DSC$K_CLASS_S;
    status = lib$getdvi(&code, 0, &dsc, &blocks);
    if (status & 1) return blocks;

    return 0;

}

int Diskquota_Remaining(char *fspec) {

    char devnam[256];
    struct fibdef fib;
    struct DQF {
    	unsigned int dqf$l_flags;
    	unsigned int dqf$l_uic;
    	unsigned int dqf$l_usage;
    	unsigned int dqf$l_permquota;
    	unsigned int dqf$l_overdraft;
    	unsigned char unused[12];} dqf1, dqf2;  /* 32-byte structure */
    struct {int length; void *pointer;} fibdsc, dqf1dsc, dqf2dsc;
    struct dsc$descriptor dsc;
    static unsigned int code = DVI$_FREEBLOCKS, jcode = JPI$_UIC;
    unsigned int status, my_uic;
    unsigned short dqflen, chan, iosb[4];
    int blocks;

    status = lib$getjpi(&jcode, 0, 0, &my_uic);
    if (!(status & 1)) return 0;

    Extract_Device_Name(fspec, devnam, sizeof(devnam));
    if (devnam[0] == '\0') return 0;

    dsc.dsc$a_pointer = devnam;
    dsc.dsc$w_length = strlen(devnam);
    dsc.dsc$b_dtype = DSC$K_DTYPE_T;
    dsc.dsc$b_class = DSC$K_CLASS_S;
    status = sys$assign(&dsc, &chan, 0, 0);
    if (!(status & 1)) return 0;

    bzero(&fib, sizeof(fib));
    bzero(&dqf1, sizeof(dqf1));
    bzero(&dqf2, sizeof(dqf2));
    fibdsc.length = sizeof(fib);
    fibdsc.pointer = &fib;
    dqf1dsc.length = sizeof(dqf1);
    dqf1dsc.pointer = &dqf1;
    dqf2dsc.length = sizeof(dqf2);
    dqf2dsc.pointer = &dqf2;

#ifndef __DECC
    fib.fib$r_exctl_overlay.fib$w_exctl = FIB$C_EXA_QUOTA;
    fib.fib$l_exsz = 0;
#else
    fib.fib$w_exctl = FIB$C_EXA_QUOTA;
    fib.fib$l_exsz = 0;
#endif
    fib.fib$l_wcc = 0;
    dqf1.dqf$l_flags = 0;
    dqf1.dqf$l_uic = my_uic;

    status = sys$qiow(0, chan, IO$_ACPCONTROL, iosb, 0, 0,
    	    &fibdsc, &dqf1dsc, &dqflen, &dqf2dsc, 0, 0);
    sys$dassgn(chan);
    if (status & 1) status = iosb[0];

    if (status == SS$_QFNOTACT ||
    	    status == SS$_NOQFILE) return -1;
    if (!(status & 1)) return 0;
    blocks = dqf2.dqf$l_permquota-dqf2.dqf$l_usage;
    return (blocks < 0) ? 0 : blocks;

}

int VMS$TrnLNM(Logical_Name, outbuf)
char *Logical_Name, *outbuf;
{
	short len;
	struct {
	    short len;
	    short code;
	    char *buf;
	    short *retlen;
	} ItmLst[] = {255, LNM$_STRING, outbuf, &len,
		      0, 0};
	char *cp,garbage[255];
    	struct dsc$descriptor dsc;
	static long attr=LNM$M_CASE_BLIND;
    	static $DESCRIPTOR(tabnam, "LNM$FILE_DEV");

	if ((cp = outbuf) == 0)
		cp = garbage;
	ItmLst[0].buf = cp;

	dsc.dsc$w_length  = strlen(Logical_Name);
	dsc.dsc$a_pointer = Logical_Name;
	dsc.dsc$b_class   = DSC$K_CLASS_S;
	dsc.dsc$b_dtype   = DSC$K_DTYPE_T;

	if (!(1&sys$trnlnm(&attr, &tabnam, &dsc, 0, ItmLst))) {
	    return(0);
	} else {
	    cp[len]= '\0';
	    return(1);
	}
}
static int get_exec_logical(char *lognam, char *eqvnam, int eqvsiz, int indx) {

    static $DESCRIPTOR(tabnam,"LNM$FILE_DEV");
    static unsigned int attr = LNM$M_CASE_BLIND;
    static unsigned int mode = PSL$C_EXEC;
    struct dsc$descriptor lnmdsc;
    unsigned short len;
    struct {
    	unsigned short bufsiz, itmcod;
    	void *bufadr, *retlen;
    } lnmlst[3];
    unsigned int status;

    lnmlst[0].bufsiz = sizeof(indx);
    lnmlst[0].itmcod = LNM$_INDEX;
    lnmlst[0].bufadr = &indx;
    lnmlst[0].retlen = 0;
    lnmlst[1].bufsiz = eqvsiz-1 > 255 ? 255 : eqvsiz-1;
    lnmlst[1].itmcod = LNM$_STRING;
    lnmlst[1].bufadr = eqvnam;
    lnmlst[1].retlen = &len;
    lnmlst[2].itmcod = lnmlst[2].bufsiz = 0;
    lnmdsc.dsc$b_dtype = DSC$K_DTYPE_T;
    lnmdsc.dsc$b_class = DSC$K_CLASS_S;
    lnmdsc.dsc$a_pointer = lognam;
    lnmdsc.dsc$w_length = strlen(lognam);
    status = sys$trnlnm(&attr,&tabnam,&lnmdsc,&mode,lnmlst);
    if ((status & 1) && len > 0) {
    	eqvnam[len] = '\0';
    	return 1;
    }

    return 0;

} /* get_exec_logical */
