/* @(#)osf.c	19.1 (ESO-DMD) 02/25/03 13:56:12 */
/*===========================================================================
  Copyright (C) 1995 European Southern Observatory (ESO)
 
  This program is free software; you can redistribute it and/or 
  modify it under the terms of the GNU General Public License as 
  published by the Free Software Foundation; either version 2 of 
  the License, or (at your option) any later version.
 
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
 
  You should have received a copy of the GNU General Public 
  License along with this program; if not, write to the Free 
  Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, 
  MA 02139, USA.
 
  Correspondence concerning ESO-MIDAS should be addressed as follows:
	Internet e-mail: midas@eso.org
	Postal address: European Southern Observatory
			Data Management Division 
			Karl-Schwarzschild-Strasse 2
			D 85748 Garching bei Muenchen 
			GERMANY
===========================================================================*/

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.TYPE        Module
.NAME        osf
.LANGUAGE    C
.AUTHOR      IPG-ESO Garching
.ENVIRONMENT VAX / VMS Version
.CATEGORY    Host operating system interfaces. File management.
.COMMENTS    
	 File management. These routines provide miscellaneous operations,
	 on files and directories. The routines use the physical file name
	 to access files. 
	 The routines return always
	 a non-negative integer number on successful return.
	 Otherwise, a value of -1 is set to indicate an error
	 condition and the variable ``oserror'' contains the 
	 symbolic error code.

.VERSION     0.0 25-Aug-1986   Definition         J. D. Ponz
.VERSION     2.0 13-Oct-1988   F. Ochsenbein. Removed logical translations,
		which do not deal only with files.
		Translation of logical names now as oshenv.
		Also, osfcreate FAILS if the file already exists!!
		Use osdopen with write option to rewrite / create a file...
.VERSION     2.1 23-Aug-1990   F. Ochsenbein. Files are created with RAT = CR.
		Take care of non-sequential files...
.VERSION    2.2	17-Oct-1990: Removed GNU-nonunderstandable code...
.VERSION    2.3	19-Feb-1992: osfinfo: filestatus modified. CG.
.VERSION    2.4	20-Aug-1992: osfinfo: filestatus modified. blocksize dropped.CG
 
 020201		last modif

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

#define DEBUG		0 	/* Set to 1 for Debugging Option	*/

#define SECTOR_SIZE	512

#include <osdefos.h>
#include <osfile.h>
#include <macrogen.h>
#include <atype.h>
#include <errno.h>	/* System	*/
#include <iodef.h>	/* System	*/
#include <stdlib.h>	/* System	*/
#	define fab$l_fna		name
#	define fab$b_fns		lname
#	define rab$l_ctx		buffer
#	define fab$l_ctx		file_no
#	define TheSector(n)		(n-1)
#	define FileSector(n)		(n+1)
#include <rms.h>	/* System	*/
#include <ssdef.h>	/* System	*/
#include <devdef.h>	/* System	*/

MID_EXTERN vmserror;		/* VMS-specific error code */
MID_EXTERN int oserror;
MID_EXTERN char *oserrmsg;

#define	FINISH		goto FIN

static struct FAB     fay, *fap;
static struct RAB     *fp;
static struct XABFHC  *fhp; 	/* File Header Characteristics  */
static struct XABPRO  *pro;	/* Protections			*/
static struct XABRDT  *rdt;	/* Revision Date & Time 	*/

static unsigned short zero  = 0;
char *osmmget();

/*==========================================================================
 *		Protection Code Changes
 *==========================================================================*/

/*==========================================================================*/
static int u_v (u)
/*++++++++
.PURPOSE Converts protection code from Unix to VMS.
.RETURNS Unix-like protection code.
.METHOD	 The VMS protection field is made of 4 hexadecimal digits,
	 edited as `WGOS'; each hexa digit contains bits `dewr' set to 1
	 to DENY access. 
 	 UNIX uses 3 octal digits `OGW', with bits `rwe' set to 1 to
	 PERMIT access.
------------------------------------------------------------*/
	int	u;	/* IN: UNIX protection code	*/
{
	register int v, i, k;

					/* Process World, Group, Owner */
  for (i=3, k=u, v=0; --i>=0; k >>= 3, v <<= 4)	
  {	v |= ( k & 2 ? 0 :10);			/* WD    permission	*/
  	v |= ( k & 4 ? 0 : 1);			/* Read	 permission	*/
  	v |= ( k & 1 ? 0 : 4);			/* Exec  permission	*/
  }

					/* Add System = Owner	*/
  v |= 	(v >> 12);

#if DEBUG
  printf("\nConverted prot. code %3o (Unix) to %4X (VMS)\n", u, v);
#endif

  return(v);
}

/*==========================================================================*/
static int v_u (v)
/*++++++++
.PURPOSE Converts protection code from VMS to Unix.
.RETURNS VMS protection code.
.METHOD	 The VMS protection field is made of 4 hexadecimal digits,
	 edited as `WGOS'; each hexa digit contains bits `dewr' set to 1
	 to DENY access. 
 	 UNIX uses 3 octal digits `OGW', with bits `rwe' set to 1 to
	 PERMIT access.
------------------------------------------------------------*/
	int	v;	/* IN: VMS protection code	*/
{
	register int u, i, k;

  k = v >> 4;				/* Suppress system protections	*/
	
					/* Process World, Group, Owner */
  for (i=3, u=0; --i>=0; k >>= 4)	
  {	u <<= 3;
	u |= ( k & 2 ? 0 : 2);			/* Write permission	*/
  	u |= ( k & 4 ? 0 : 1);			/* Read	 permission	*/
  	u |= ( k & 1 ? 0 : 4);			/* Exec  permission	*/
  }

#if DEBUG
  printf("\nConverted prot. code %4X (VMS) to %3o (Unix)\n", v, u);
#endif

  return(u);
}

/*======================================================================*/
static long vmstime(q)
/*+++++++++++++ 
.PURPOSE	Converts the VAX time to standard
.RETURNS	Time as seconds elapsed since Jan. 1, 1970
.REMARKS	The VAX time is 64 bits; origin = Nov. 17, 1858; units = 100ns.
		(40587 days before Jan 1, 1970)
--------------*/
	unsigned long *q;	/* IN: VAX time	*/
{
	register double tt;
	register long t;

  tt = q[1] * 4294967296e0;
  tt = tt + q[0];
  tt = tt / 1.e7 - (40587.e0 * 86400.e0);

  t = tt;
  
  return(t);
}

/*======================================================================*/
static int finit (phname)
/*++++++++
.PURPOSE Initializes the RMS blocks
.RETURNS 0
------------------------------------------------------------*/
	char *phname;		/* IN : physical filename */
{
	struct RAB *vmsinit();

  fp = vmsinit(phname);
  fap = fp->rab$l_fab;
  fap->fab$b_fac = FAB$M_BRO|FAB$M_PUT;
  fhp = (struct XABFHC *)fap->fab$l_xab; /* File Header Characteristics */
  pro = (struct XABPRO *)fhp->xab$l_nxt; /* Next block = protection	*/
  rdt = (struct XABRDT *)pro->xab$l_nxt; /* Next block = date/time	*/

  return(0);
}

/*==========================================================================
 *		osf Routines
 *==========================================================================*/


/*==========================================================================*/
int osfcreate(phname, nobyt, protec)
/*++++++++
.PURPOSE Creates a NEW file (STMLF format by default).
         Access is done by physical file name. Owner and group identifications
         of the process creating the file define the owner and group 
         identification of the file.
         The argument protec gives the file protection code, in Unix form.
.RETURNS 0 upon successful completion.
         Else , -1 is returned and the system error code is set into
         oserror. Note that trying to create a new file with the same
         name as an existing file is an error.
.REMARKS The protection code may also include the NOATTR option.

	The protection code is made of 3 octal digits `OGW' for Owner / 
	Group / World; each octal digit is made of 3 bits `rwe' 
	for read / write (delete) / execute protection set to 1 to
	 PERMIT access. As an example, 0755 allows read / execute
	 to everybody, but rewriting the file is only allowed to the owner.
	The protection code of 0 converted to default 0755 protection.
	
	To create non-STMLF files, use osfop before oscfreate.
----------------------------------*/
	char *phname;	/* IN : physical filename */
	long  nobyt;	/* IN : number of bytes to be allocated */
	int   protec;  	/* IN : protection code `OGW' (3 octal digits) \
				and NOATTR option	*/
{
	register int i;
	MID_STATIC char c = '\n'; 	/* Character written in file*/

  oserror = 0;
  if (nobyt < 0)		nobyt = 0;
  if (protec == 0)		protec = 0755;

  finit(phname);
  if (protec & NOATTR)	;
  else	fap->fab$b_rat = FAB$M_CR;	/* Create file With RAT	*/
  i = (nobyt + SECTOR_SIZE - 1) / SECTOR_SIZE;
  /* if (fap->fab$b_org == FAB$C_SEQ)*/	fap->fab$l_alq = i;

  pro->xab$w_pro = u_v(protec);		/* Convert Protection Code	*/

  vmserror = SYS$CREATE(fap);		/* Create the file ...		*/
  if (vmserror != RMS$_NORMAL)	FINISH;

  if (nobyt && (fap->fab$b_org == FAB$C_SEQ)) {	/* Write if necessary	*/
  	vmserror = SYS$CONNECT(fp);
	if (vmserror != RMS$_NORMAL)	FINISH;

	fp->rab$l_bkt = i;		/* Last block ...	*/
	fp->rab$w_rsz = nobyt - SECTOR_SIZE*(i-1);
	fp->rab$l_rbf = osmmget(fp->rab$w_rsz); 	
	LIB$MOVC5(&zero, 0, &zero, &fp->rab$w_rsz, fp->rab$l_rbf);
							/* Fill with zeroes */
	*(fp->rab$l_rbf + fp->rab$w_rsz -1) = c;
	vmserror = SYS$WRITE(fp);
	if (vmserror != RMS$_NORMAL)	FINISH;
	osmmfree(fp->rab$l_rbf);
  }
  vmserror = SYS$CLOSE(fap);		/* Close the file ...		*/
  if (vmserror != RMS$_NORMAL)	FINISH;

  FIN:
  if (vmserror != RMS$_NORMAL)		oserror = EIO;
  return( oserror ? -1 : 0);
}

/*==========================================================================*/
int osfdelete(phname)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Deletes file on disk by removing the directory entry corresponding
         to the physical name. The file is assumed not to be open.
.RETURNS Value 0 on normal return /  -1 if something wrong happened.
         ``oserror'' is then set to the system error code.
.REMARKS 
------------------------------------------------------------*/
	char *phname;			/* IN : physical filename */
{ 
  oserror = 0;

  finit(phname);
  vmserror = SYS$ERASE(fap);
  if (vmserror != RMS$_NORMAL)		oserror = ENOENT;

  return(oserror ? -1 : 0);
}

/*==========================================================================*/
int osfcontrol(phname, function, value1, value2)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Change file protection and/or owner. Access to the
         file is by physical name. The argument function defines the
         operation to be done, value1 is the protection code (!! in octal)
	 or the owner code and value2 is 0 or the actual group identification.
.RETURNS Value 0 on normal return. Value -1 in case of error (sys error 
         number is then returned to oserror).
.METHOD  UIC on VMS is made of (group<<16 + owner)
.REMARKS 
------------------------------------------------------------*/
	char *phname;			/* IN : physical filename */
	int function;			/* IN : function code */
	int value1;			/* IN : protection or owner code */
	int value2;                     /* IN : 0          or group code */
{
  oserror = 0;
    
  finit(phname);
  vmserror = SYS$OPEN(fap);		/* Open first the file ...	*/
  if (vmserror != RMS$_NORMAL)	FINISH;

  switch (function) 
  { case CHMOD :                	/* change file protection  	*/
	pro->xab$w_pro = u_v(value1);	/* Convert Protection Code	*/
	vmserror = SYS$CLOSE(fap);	/* and close it ...		*/
	break;

    case CHOWN :  			/* change owner and group of the file */
	pro->xab$l_uic = value1 + (value2<<16);	/* Convert Owner UIC	*/
	vmserror = SYS$CLOSE(fap);	/* and close it ...		*/
	break;

    default:
	oserror = EINVAL;
	break;  
  }

  FIN:
  if (vmserror != RMS$_NORMAL)		oserror = EIO;
  return(oserror ? -1 : 0);
}

int osfinfo(phname, status)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Get file status information. Access to the file is by physical name.
.RETURNS Value 0 on normal return. (-1 if file does not exist).
.REMARKS 
------------------------------------------------------------*/
	char *phname;  			/* IN : physical filename */
	struct filestatus *status; 	/* OUT : output file status */
{
  oserror = 0;

  finit(phname);
  fap->fab$b_fac = FAB$M_BRO|FAB$M_GET;
  fap->fab$b_shr = FAB$M_SHRGET;
  vmserror = SYS$OPEN(fap);		/* Open first the file ...	*/
  if (vmserror != RMS$_NORMAL)	FINISH;
  status->filesize = (fhp->xab$l_ebk-1)*fap->fab$w_bls + fhp->xab$w_ffb; 
  if (status->filesize < 0) 	status->filesize = 0;
  status->date	   = vmstime(&rdt->xab$q_rdt);	/* Convert the date/time  */
  status->owner    = pro->xab$l_uic;
  status->protection = v_u(pro->xab$w_pro);	/* Convert Prot. Code	  */
/*  status->blocksize  = (fap->fab$w_bls ? fap->fab$w_bls : SECTOR_SIZE); */
  vmserror = SYS$CLOSE(fap);		/* and close it ...		*/

  FIN:
  if (vmserror != RMS$_NORMAL)		oserror = ENOENT;

  return(oserror ? -1 : 0);
}

/*==========================================================================*/
long osfdate(phname)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Get the date of last modification of a file.
.RETURNS Date of last modification (-1 if file does not exist).
.REMARKS 
------------------------------------------------------------*/
	char *phname;  			/* IN : physical filename */
{
	long date;
	
  oserror = 0;

  finit(phname);
  fap->fab$b_fac = FAB$M_BRO|FAB$M_GET;
  fap->fab$b_shr = FAB$M_SHRGET;
  vmserror = SYS$OPEN(fap);		/* Open first the file ...	*/
  if (vmserror != RMS$_NORMAL)	FINISH;

  date	   = vmstime(&rdt->xab$q_rdt);	/* Convert the date/time  */

  vmserror = SYS$CLOSE(fap);		/* and close it ...		*/

  FIN:
  if (vmserror != RMS$_NORMAL)		oserror = ENOENT;

  return(oserror ? -1 : date);
}

/*==========================================================================*/
long osfsize(phname)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Get the size of a file.
.RETURNS Size of file in bytes (-1 if file does not exist).
.REMARKS 
------------------------------------------------------------*/
	char *phname;  			/* IN : physical filename */
{
	long size;
	
  oserror = 0;

  finit(phname);
  fap->fab$b_fac = FAB$M_BRO|FAB$M_GET;
  fap->fab$b_shr = FAB$M_SHRGET;
  vmserror = SYS$OPEN(fap);		/* Open first the file ...	*/
  if (vmserror != RMS$_NORMAL)	FINISH;

  size = (fhp->xab$l_ebk-1)*fap->fab$w_bls + fhp->xab$w_ffb; 
  if (size < 0) size = 0;

  vmserror = SYS$CLOSE(fap);		/* and close it ...		*/

  FIN:
  if (vmserror != RMS$_NORMAL)		oserror = ENOENT;

  return(oserror ? -1 : size);
}

/*==========================================================================*/
int osfrename(oldname, newname)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Renames a disk file by giving old and new physical names.
.RETURNS Value 0 on normal return. (-1 on error. Oserror is then set to the
	actual error code).
.REMARKS 
------------------------------------------------------------*/
	char *oldname;			/* IN : old physical filename */
	char *newname;			/* IN : new physical filename */
{
  oserror = 0;

  finit(oldname);
  fay = cc$rms_fab; 
  if (newname)				/* Add name of file		*/
  { 	fay.fab$b_fns = strlen(newname);/* sets length of file_name 	*/
	fay.fab$l_fna = newname;
  }
  vmserror = SYS$RENAME(fap, 0, 0, &fay);

  FIN:
  if (vmserror != RMS$_NORMAL)		oserror = ENOENT;

  return(oserror ? -1 : 0);
}

int osftranslate(logname, phname)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Translate logical names into physical names. i.e. $KEYS ==> myfile.key
.METHOD  Searches the system variable list for the logname.
.RETURNS length of physical name on normal return,
         -1 when logname does not exist (phname empty).
.REMARKS System dependencies:
 --UNIX: getenv(3).
------------------------------------------------------------*/
char *logname;                  /* IN : logical name */
char *phname;                   /* OUT: translated physical filename */

{
register int nr;

char *getenv();
register char *t, *ptr, cc;



/* Do the logical translation */

if ((t = getenv(logname)) == NULL)
   {
   *phname = '\0';
   return(-1);
   }

ptr = phname;
for (nr=0; ; nr++)              /* copy phys. filename */
   {
   cc = *t++;
   if (cc == '\0')
      {
      *ptr = '\0';
      return (nr);
      }
   *ptr++ = cc;
   }
}

int osfphname(logname, phname)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Translate logical names into physical names. i.e. $KEYS ==> myfile.key
.METHOD  Searches the system variable list for the logname.
.RETURNS Value 0 on normal return, -1 when logname does not exist 
         (phname empty). 
.REMARKS System dependencies:
 --UNIX: getenv(3).
------------------------------------------------------------*/
char *logname;			/* IN : logical name */
char *phname;			/* OUT: translated physical filename */
{
	char *getenv();
	register char *t1;

	/* 
	** Do the logical translation 
	*/
	if ((t1 = getenv(logname)) == NULL){
		*phname = '\0';
		return(-1);
		}
	strcpy(phname,t1);
	return(0);
}
 

