/* @(#)osd.c	19.1 (ESO-DMD) 02/25/03 13:56:11 */
/*===========================================================================
  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        osd.c
.LANGUAGE    C
.AUTHOR      IPG-ESO Garching
.CATEGORY    Host operating system interfaces. i/o on disk files.
.ENVIRONMENT VAX / VMS
.COMMENTS     Handling of disk io's. 
         All files are referenced by the physical name in the open function. 
         A file identifier (number)
         is returned by open functions to be used in later i/o operations.
\begin{TeX}
This module includes {\em osd} routines which perform
access to disk files. Possible seeking at byte address.
The buffering is done within this routine, and a
large blocking {\em LARGE\_BUFFER} option is also available.
Note that only {\em STMLF} format files are
unix-compatible! For other format, use preferably the {\em osa} routines...

The routines return always
a non-negative integer number on successful return.
Otherwise, a value of -1 is set to indicate an error
condition or the End of File; the variable ``oserror'' 
contains the numeric error code ($0$ for EOF), 
which can be converted to a character pointer via {\em osmsg routine}.

\end{TeX}

.VERSION 0.0 	25-Aug-1986   Definition     J. D. Ponz
.VERSION 2.0 	13-Oct-1988   VMS version (F. Ochsenbein)
.VERSION 2.1 	10-Jan-1989   Coorected bug in osdopen (propagate NOATTR option)
.VERSION 2.2 	24-Aug-1990   Corrected bug in osdwrite (very large blocks).
				Allow non-sequential organisation.
.VERSION 2.3 	19-Nov-1990   Force multiple of 8 bytes for buffers.
.VERSION 2.4 	23-Apr-1991   Be sure to free buffer memory at osdclose...
------------------------------------------------------------*/

#define IO_MAX		(90*512)	/* Set to 16 FITS blocks */

#define DEBUG	0
#include <osdefos.h>
#include <osfile.h>
#include <macrogen.h>
#include <atype.h>
#include <errno.h>	/* System	*/
#include <iodef.h>	/* System	*/
#	define fab$l_fna	name
#	define fab$b_fns	lname
#	define rab$l_ctx	buffer	/* Used as address of Buffer */
#	define fab$l_ctx	file_no
#include <rms.h>	/* System	*/
#include <ssdef.h>	/* System	*/
#include <devdef.h>	/* System	*/

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

#define	FINISH		goto FIN

static struct FAB  	*fap;
static struct XABFHC	*fhp;
static struct RAB 	*fp;
static int TheFile = 0;
	
static unsigned short 	chan;	/* Set by getfile, tells QIO's to be used */
static short	move_len;

struct RAB *vmsrab();
long   vmsize();

struct Buffer {
	int size, offset, bytes, block;
	unsigned long file_size;
	unsigned char buffer_modified;
	unsigned char flush;
	unsigned char spare[2];
	char *text;
 	};
static struct Buffer *b;
static unsigned long full_size;
static char gbuf = 1;		/* In getfile(), ask to allocate buffer */
	
/*==========================================================================*/
static int vmsread(pbuf, nochar)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Interface to SYS$READ
.RETURNS Number of characters actually read / -1 if an error is encountered /
	0 if the end of file is reached. 
	In case of error, oserror is set to the relevant error number.
.REMARKS Works only on Disk Files ! The virtual sector to read is
	b->block. Also, nochar is assumed to be POSITIVE ...
------------------------------------------------------------*/
	char   *pbuf;		/* OUT : pointer to i/o buffer */
	int    nochar;		/* IN : size of i/o buffer */
{
	register    int len;

  if (nochar <= 0)	{ oserror = EINVAL; FINISH; }
  fp->rab$l_bkt = b->block + 1;
  fp->rab$l_ubf = pbuf; 
  fp->rab$w_usz = nochar;

  vmserror = SYS$READ(fp);

  len = fp->rab$w_rsz;		/* Length of read block	*/
  switch(vmserror)
  { case RMS$_NORMAL:		/* Compute Next Block	*/
	break;
    case RMS$_EOF:	len =  0;	break;
    default:		oserror = EIO;	break;
  }

  FIN:
#if DEBUG
    printf("vmsread file %d rab$l_bkt #%d: bytes=%d on %d\n",
    	TheFile, fp->rab$l_bkt-1, len, nochar);
#endif
  return(oserror ? -1 : len);
}

/*==========================================================================*/
static int vmswrite(pbuf, nochar)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Interface to SYS$PUT.
.RETURNS Number of characters actually written / -1 if an error is encountered.
	In case of error, oserror is set to the relevant error number.
.REMARKS Works only on Disk Files ! The virtual sector to read is
	b->block. Also, nochar is assumed to be POSITIVE ...
------------------------------------------------------------*/
	char *pbuf;			/* IN : pointer to i/o buffer */
	int  nochar;	        	/* IN : size of i/o buffer */
{
	register int len;

  fp->rab$l_bkt = b->block + 1;
  fp->rab$l_rbf = pbuf; 
  fp->rab$w_rsz = nochar;

  vmserror = SYS$WRITE(fp);

  if (!(vmserror&1))	oserror = EIO;
  else			len = nochar;

  FIN:
#if DEBUG
    printf("vmswritten file %d: bytes=%d on %d - Block is now %d\n",
    	TheFile, len, nochar, fp->rab$l_bkt);
#endif
  return(oserror ? -1 : len);
}


		/****************************
		 * General Utility Routines *
	 	 ****************************/

/*==========================================================================*/
static int osbuffer(size)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Open a buffer for file fid.
.RETURNS size of buffer (success) / -1 (failure)
------------------------------------------------------------*/
	int   size;	/* IN : size of buffer (bytes) */
{

  b = (struct Buffer *)osmmget(size + sizeof(struct Buffer));
  if (oserror)			FINISH;

  fp->buffer = (unsigned int) b;  /* casted by tm 05.09.89 */
  b->text   = (char *) ( b + 1);
  b->size   = size;
  b->offset = 0;
  b->bytes  = 0;
  b->block  = 0;	/* Nothing was read	*/
  b->buffer_modified = 0;
  b->flush           = 0;
  b->file_size = full_size;

  FIN:
  return (oserror ? -1 : size);
}

/*==========================================================================*/
static int getfile(fid)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Retrieves the file pointer.
.RETURNS File number (fid); the static fp (file pointer) is also set up.
------------------------------------------------------------*/
	int   fid;			/* IN : file identifier */
{
  if (!(fp = vmsrab(fid))) 	FINISH;

  b   = NULL_PTR(struct Buffer);
  fap = fp->rab$l_fab;
  fhp = (struct XABFHC *)(fap->fab$l_xab);
  TheFile = fap->file_no;

  if (fp->rab$l_ctx == 0xffffffff) {	/* Indicator for QIO usage */
  	chan = fp->rab$w_isi;
  }
  else	{
  	chan = 0, b = (struct Buffer *)fp->buffer;
	if(gbuf && (!b))		/* Allocate Buffer if Necessary	*/
  	{	full_size = (fhp->xab$l_ebk > 0 ? 
  			(fhp->xab$l_ebk-1)*fap->fab$w_bls + fhp->xab$w_ffb :
  			0);
		osbuffer(fap->fab$w_bls);
    		b->block = 0;
	}
  }

  FIN:
  return (oserror ? -1 : fid);
}

/*==========================================================================*/
static int oswb()
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Write the Buffer to disk (called by osd routines)
.RETURNS Bytes written / -1 for error (oserror provides an explanation)
------------------------------------------------------------*/
{
    register	int 	len;
    unsigned long fsize;

#if DEBUG
  printf("....Entering oswb: block=%d, size=%d, bytes=%d, offset=%d, file_size=%d, flag=%d\n", 
  	b->block, b->size, b->bytes, b->offset, b->file_size, b->buffer_modified);
#endif
  len = 0;

  if (b->buffer_modified) 			/* Write Block	*/
  {	fsize = b->block*fap->fab$w_bls + b->bytes;
	if (b->file_size < fsize) 	b->file_size = fsize;
  	if ((len = vmswrite(b->text, b->bytes)) < 0)	FINISH;
	b->buffer_modified = 0;
#if DEBUG
  printf("....Exiting  oswb: block=%d, size=%d, bytes=%d, offset=%d, file_size=%d, flag=%d\n", 
  	b->block, b->size, b->bytes, b->offset, b->file_size, b->buffer_modified);
#endif
  }

  FIN:
  return (oserror ? -1 : len);
}

/*==========================================================================*/
static int osrb()
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Read the Next Buffer from disk (called by osd routines)
.RETURNS Length of read block, 0 for EOF, -1 for errors.
.REMARKS The current is rewritten on disk, if it was modified.
------------------------------------------------------------*/
{
    register int i;

	/* Read now one Buffer	*/

#if DEBUG
  printf("....Entering osrb: block=%d, size=%d, bytes=%d, offset=%d, file_size=%d, flag=%d\n", 
  	b->block, b->size, b->bytes, b->offset, b->file_size, b->buffer_modified);
#endif
  if (b->buffer_modified)
  	if (oswb() < 0)			FINISH;
  if ((b->block * fap->fab$w_bls + b->bytes) >= b->file_size)
  				{ i= 0; FINISH; }	/* EOF */
  b->block += (b->bytes / fap->fab$w_bls);
  b->bytes  = 0;
  b->offset = 0;
  i = vmsread(b->text, b->size);
  if (i < 0)				FINISH;
  b->bytes  = i;
#if DEBUG
  printf("....Exiting  osrb: block=%d, size=%d, bytes=%d, offset=%d, file_size=%d, flag=%d\n", 
  	b->block, b->size, b->bytes, b->offset, b->file_size, b->buffer_modified);
#endif

  FIN:
  return (oserror ? -1 : i);
}


		/************************
		 *	OSD Routines	*
		 ************************/


/*==========================================================================*/
int osdopen(phname, mode)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Opens disk file for read or write, and positions at 
	file beginning (at end for APPEND mode).
	The variable `mode' defines the way of opening the file.
.RETURNS Upon successful completion a file descriptor is returned. -1 is
         returned on error.
.REMARKS The mode is a combination of the following options 
\begin{TeX}
	(defined in {\tt osfile.h}):
\begin{itemize}
\item {\em READ  WRITE  READ\_WRITE  APPEND}
\item {\em NOATTR} on VMS only (record without attributes, i.e. without linefeed)
\item {\em LARGE\_BUFFER} for better efficiency when randomly accessing 
	large sections files.
\end{itemize}
	The routine {\em osfop}, if called before {\em osdopen},
	allows to use non STMLF record formats.
	Note that e.g. variable-length record are not interpreted:
	the file is seen as a stream !!
\end{TeX}
------------------------------------------------------------*/
	char *phname;			/* IN : physical filename */
	int  mode;	/* IN : open mode (see remarks) */
{
	register int i, bs, bsize;
    
  if ((i = vmsopen(phname, mode)) < 0)		FINISH;

  				/* Check Special File Organisation	*/
  fp = vmsrab(i);
  if (fp->rab$l_ctx == 0xffffffff)	FINISH;		/* Not a Disk 	*/
  fap = fp->rab$l_fab;
				/* Take care of non-sequential files.
				   Close / Reopen to use QIO's only.	*/
  if (fap->fab$b_org != FAB$C_SEQ) {
	bs = vmsopen ((char *)-1, 0);
	if (bs < 0)	FINISH;
  	fp->rab$l_bkt  = 1;		/* Virtual Block */
  	fp->rab$l_ctx  = 0xffffffff;			/* Use Qio	*/
    	fp->rab$w_isi = bs;
	fap->fab$l_fop = FAB$M_UFO;
    	FINISH;
  }

  
  bs = gbuf; gbuf = 0;			/* Don't allocate buffers */
  getfile(i);
  gbuf = bs;

	/* Determine the size of buffers */

  bs = (mode&LARGE_BUFFER ? IO_MAX/fap->fab$w_bls : 1);
  full_size = vmsize(TheFile);
  bsize = fap->fab$w_bls * bs;
  if ((mode&3) == 0) {
  	bsize = MIN(bsize, full_size);
  	bsize = ((bsize+7)>>3)<<3;	/* Take multiple of 8 bytes */
  }
  if (osbuffer(bsize) < 0)	FINISH;
  
  switch(mode&3) {
    case APPEND:
  	b->block = b->file_size/fap->fab$w_bls;
	if (osrb() < 0)		FINISH;
	b->offset = b->bytes;
	break;
    case READ_WRITE:
	if (osrb() < 0)		FINISH;
	break;
    case WRITE:	
    	b->block = 0;
	break;
  }

  FIN:
  return (oserror ? -1 : i);
}

/*==========================================================================*/
int osdclose(fid)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Closes a disk file (previously opened by osdopen)
.RETURNS 0 (success) / -1 (failure)
------------------------------------------------------------*/
	int   fid;		/* IN : file identifier */
{
  if (getfile(fid) < 0)		FINISH;

  if (chan == 0)
  	if (oswb() < 0)		FINISH;		/* Write last buffer	*/

  osmmfree(fp->buffer);				/* Free Buffer		*/
  vmsclose(TheFile);

  FIN:
  return (oserror ? -1 : 0);
}

/*==========================================================================*/
int osdwait(fid)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Force the physical writing on disk of modified buffers.
         The argument fid is the file identification obtained from osdopen.
.RETURNS 0 (success) / -1 (failure)
.REMARKS 
------------------------------------------------------------*/
	int   fid;			/* IN : file identifier */
{
  if (getfile(fid) < 0)		FINISH;

  if (chan)			FINISH;
  
  if (oswb() < 0)		FINISH;
  
  FIN:
  return (oserror ? -1 : 0);
}

/*==========================================================================*/
int osdread(fid, pbuf, nochar)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Synchronous read from a file.
         The argument fid is the file identification obtained from osdopen.
         The routine reads nochar bytes starting at at the current
	byte position.
.RETURNS --- Number of characters actually read.
         --- -1 if an error is encountered or if the end of file is
	 reached. In case of error, oserror is set to the relevant error
	 number; oserror is set to 0 in case of EOF.
.REMARKS It's a straight read, i.e. reading variable-length record files
 	provide stange results...
------------------------------------------------------------*/
	int    fid;			/* IN : file identifier */
	char   *pbuf;			/* IN : pointer to i/o buffer */
	unsigned int    nochar;		/* IN : buffer size */
{
	register    int i, len;
	register char *p, *pe;

  if (getfile(fid) < 0)		FINISH;

  p = pbuf, pe = p + nochar;
  if (nochar == 0)		FINISH;		/* Read 0 bytes ... */

  if (chan) {	
	if (fap->fab$l_fop & FAB$M_UFO) {
		len = vmsqio (chan, IO$_READVBLK, pbuf, nochar, fp->rab$l_bkt);
		if (len <= 0)	FINISH;
  		fp->rab$l_bkt  += (len + 511)>>9;
	}
  	else	len = vmsrlb(chan, pbuf, nochar);
  	p += len;
  	FINISH;
  }

				/* Get first Data existing in Buffer	*/
  if ((b->block*fap->fab$w_bls + b->offset) >= b->file_size)
				FINISH;			/* Beyond EOF	*/

  if (b->bytes > b->offset)
  {	len = MIN(nochar, b->bytes - b->offset);
  	move_len = len, LIB$MOVC3(&move_len, b->text + b->offset, p);
	b->offset += len, p += len;
	if (len == nochar)	FINISH;
  }

				/* Read Now Directly into user's buffer */
  if (b->buffer_modified)	if (oswb() < 0)	FINISH;
  if ((b->block*fap->fab$w_bls + b->offset) >= b->file_size)
				FINISH;			/* Beyond EOF	*/
  b->block += (b->offset / fap->fab$w_bls);
  b->bytes = 0, b->offset = 0;

  while( (pe - p) >= b->size)
  {	len = MIN((pe-p),IO_MAX) / b->size;
	len *= b->size;
	i = vmsread(p, len);
	if (i <= 0)	FINISH;
	b->block += (i / fap->fab$w_bls);
	p += i;
	if (i < len)		/* EOF Encountered */
	{ 	b->offset = i % fap->fab$w_bls; 
		FINISH;	
	}
  }
  if (p == pe)		FINISH;	/* All is read	*/

					/* Read now one Buffer	*/
  if (osrb() <= 0)		FINISH;

			/* Get remaining bytes from new buffer	*/
  len = MIN(pe-p, b->bytes);		/* b->offset is zero... */
  move_len = len, LIB$MOVC3(&move_len, b->text, p);
  b->offset += len, p += len;

  FIN:
  if (oserror)	len = -1;
  else		len = p - pbuf;
  return(len == 0 ? -1 : len);
}

/*==========================================================================*/
int osdputs(fid, pbuf)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Synchronous write to a file a string of characters followed
	by a newline (end of record).
         The argument fid is the file identification obtained from osdopen.
.RETURNS Number of characters actually written / -1 for error 
	(oserror provides an explanation)
.REMARKS 
------------------------------------------------------------*/
	int  fid;		/* IN : file identifier */
	char *pbuf;		/* IN : EOS-terminated string to write */
{
	register char *p;
	register int  i;

  i  = (int) strlen(pbuf);
  p  = pbuf + i++;
  *p = '\n';

  i  = osdwrite(fid, pbuf, i);
  *p = EOS;

  FIN:
  return (oserror ? -1 : i);
}

/*==========================================================================*/
int osdwrite(fid, pbuf, nochar)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Synchronous write to a disk file.
         The argument fid is the file identification obtained from osdopen.
         The routine writes nochar bytes from the buffer pointed
         by pbuf to file, at the current byte position.
.RETURNS Number of characters actually written. 
	-1 for error (oserror provides an explanation)
------------------------------------------------------------*/
	int  fid;			/* IN : file identifier */
	char *pbuf;			/* IN : pointer to i/o buffer */
	unsigned int  nochar;		/* IN : number of i/o characters */
{
    register    int len;
    char 	*p, *pe, *p1;
    unsigned long fsize;

  if (nochar == 0)		FINISH;
  if (getfile(fid) < 0)		FINISH;

  p = pbuf, pe = pbuf+nochar, len = 0;

			/* Take care first which system service to use	*/
  if (chan) {					/* Output via QIO	*/
	if (fap->fab$l_fop & FAB$M_UFO) {
		len = vmsqio (chan, IO$_WRITEVBLK, pbuf, nochar, fp->rab$l_bkt);
		if (len <= 0)	FINISH;
  		fp->rab$l_bkt  += (len + 511)>>9;
	}
  	else 	if (fid > 2)	len = vmsqio(chan, IO$_WRITELBLK, pbuf, len);
	else	while (p < pe)	{	/* Map \n to \r\n	*/
		p1 = p + 1;
		if (p1 < pe)	p1 += oscloc(p1, pe-p1, '\n');
		if (p1 < pe)
			*p1 = '\r', 
			len += vmsqio(chan, IO$_WRITELBLK, p, (p1-p)+1), 
			*p1 = '\n';
		else	p1  = pe,   
			len += vmsqio(chan, IO$_WRITELBLK, p, (p1-p));
		p = p1;
	}
  	FINISH;
  }

			/* Buffered File: move data first into Buffer	*/
  len = MIN((pe-p), b->size - b->offset);
  if (len)
  {	move_len = len, LIB$MOVC3(&move_len, p, b->text + b->offset);
  	p += len, b->offset += len;
  }

  if (b->bytes < b->offset)	b->bytes = b->offset;
  if (p > pbuf)			b->buffer_modified = 1;
  if (pe == p)		goto FLUSH;	/* All is written */

				/* ... write the filled buffer ...	*/
  if (b->offset == b->size)	
  	if (oswb() < 0)		FINISH;

  b->block += (b->bytes / fap->fab$w_bls);
  b->bytes = 0, b->offset = 0;

				/* ... Write Directly to File ...	*/
  for (; (pe-p) >= b->size; p += len)
  {	len = MIN((pe-p), IO_MAX) / b->size;
	len *= b->size;
	len = vmswrite(p, len);
	if (len < 0)		FINISH;
	b->block += (len / fap->fab$w_bls);
  }

  if (pe == p)		goto FLUSH;	/* All is written */

				/* ... copy rest to empty Buffer ...	*/
  if (osrb() < 0)		FINISH;
  b->offset = pe - p;
  move_len = b->offset, LIB$MOVC3(&move_len, p, b->text);
  p += 	b->offset;
  if (b->bytes < b->offset)	b->bytes = b->offset;
  if (b->offset)		b->buffer_modified = 1;

  FLUSH:			/* Rewrite buffer to file */
  fsize = b->block*fap->fab$w_bls + b->bytes;
  if (b->file_size < fsize) 	b->file_size = fsize;
  len = p - pbuf;
  if (b->flush)  		osdwait(fid);

  FIN:
  return (oserror ? -1 : len);
}

/*==========================================================================*/
long osdseek(fid, address, mode)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Moves read/write pointer to a specified byte position.
	Bytes are numbered from 0 at the file beginning.
	The address is a displacement in BYTES from
	start of the file, current position or end of the file as
	defined by the variable mode (FILE_START, FILE_CURRENT, FILE_END).
	The argument fid is the file identification obtained from osdopen.
.RETURNS Upon successful completion, the new byte position;
	-1 on error (oserror explains the error).
------------------------------------------------------------*/
	int  fid;			/* IN : file identifier */
	long address;			/* IN : Move in Bytes	*/
	int  mode;			/* IN : mode of addressing */
{
	long pos, sector, posf;

  if (getfile(fid) < 0)		FINISH;

  if (chan)		{ oserror = ESPIPE; FINISH;}

	/* Compute present position for Buffered File */

  switch (mode) 
  { case FILE_CURRENT :		/* seek to file_current + address */
	pos = b->block*fap->fab$w_bls + b->offset;
	if (address == 0)	FINISH;
	pos += address;		/* Absolute address, continue	*/
	break;
    case FILE_START : 		/* seek to file_start + address */
	pos = address;
	break;
    case FILE_END : 		/* seek to file_end + address 	*/
	pos = b->file_size + address;
	break;
    default:
	oserror = EINVAL;
	break;
  }

  if (oserror)		FINISH;
  if (pos < 0)		{ oserror = ESPIPE; FINISH;}
  sector = pos / fap->fab$w_bls;
  posf = pos - b->block * fap->fab$w_bls;	/* Offset in Buffer */

				/* Test block is loaded */
  if (( posf >= 0) && (posf <= b->bytes))
  {	b->offset = posf;
  	FINISH;
  }

			/* ... Must set position to a new sector ...	*/
  if (b->buffer_modified)
  	if (oswb() < 0)		FINISH;	/* Rewrite first the Sector...	*/
  b->offset = 0, b->bytes = 0;
  b->block = sector;			/* Position to sector ...	*/
  posf = pos - sector*fap->fab$w_bls;	/*	Fraction of sector	*/
  if (posf == 0)	FINISH;		/* ... if at boundary, it's OK	*/
  if (osrb() < 0)	FINISH; 	/* ... read the correct sector	*/
  b->offset = posf;
  
  FIN:
  return (oserror ? -1 : pos);
}

/*==========================================================================*/
long osdsize(fid)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Tells the size of an opened file without moving the file pointer.
.RETURNS Upon successful completion, the file size;
	-1 on error (oserror explains the error).
.REMARKS 
------------------------------------------------------------*/
	int  fid;			/* IN : file identifier */
{
  if (getfile(fid) < 0)		FINISH;

  if (chan)		{ oserror = ESPIPE; FINISH;}

  FIN:
  return (oserror ? -1 : b->file_size);
}

/*==========================================================================*/
int osdunix(fid)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Tells if the opened file is Unix-compatible
.RETURNS 0 if non-unix, 1 if Unix, -1 on error (oserror explains the error).
.REMARKS VMS only
------------------------------------------------------------*/
	int  fid;			/* IN : file identifier */
{
	register int isunix;
	
  if (getfile(fid) < 0)		return(-1);

  return ((fap->fab$b_rfm == FAB$C_STMLF) && (fap->fab$b_org = FAB$C_SEQ));
}
