/*===========================================================================
  Copyright (C) 1986-2009 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 Massachusetss Ave, Cambridge, 
  MA 02139, USA.
 
  Corresponding 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           file.c
.LANGUAGE       C
.AUTHOR         Alan Richmond [ST-ECF], Francois Ochsenbein [ESO-IPG]
.CATEGORY       Interface to disk file access
.ENVIRONMENT    
.COMMENTS       To improve file handling independance.
                The UNIX i/o functions are used; for output,
                creat generates standard C (i.e. byte stream) files.
\begin{TeX}
The basic operations are performed by means of
\begin{itemize}
\item fi\_open(filename,mode) to open a file, with mode values
        defined in <filedef.h>
\item fi\_close to close a file
\item fi\_name to retrieve the name of a file
\item fi\_load to load efficiently ascii text from a file
\item fi\_seek to position to a specified byte position (from the file
        beginning, end, or current position)
\item fi\_tell to return the current byte location within a file.
\item to read records, use
        \begin{itemize}
        \item fi\_read reads a specified amount of bytes
        \item fi\_gets reads a record, terminating the record with an EOS;
                a returned status indicates if a full record was read
        \end{itemize}
\item to write records, use     
        \begin{itemize}
        \item fi\_write writes a specified amount of bytes 
        \item fi\_put writes an EOS-terminated ASCII string
        \item fi\_puts writes a record (the EOS terminator is replaced
                by an EOL character)
        \end{itemize}
\end{itemize}
\end{TeX}
.VERSION 1.0	Creation Alan Richmond
.VERSION 2.0	01-Aug-1986: VMS dependancies are grouped within #if VMS blocks.
	Buffering is used for byte stream files.
.VERSION 2.1	30-Sep-1986: Be sure that the last character is not lost, even
	on VMS-files.
.VERSION 2.2 	16-Dec-1986: Defined internal functions as static
.VERSION 2.3 	17-Feb-1987: Added fi_wopen
.VERSION 3.0	13-Oct-1988: Use os interfaces
.VERSION 3.1	07-Dec-1988: Re-added fi_wopen...
.VERSION 3.2	20-Jun-1989: Translate file name before Open
.VERSION 3.3	20-Nov-1989: Be sure that fi_load ends with EOS.
.VERSION 3.4	14-Dec-1989: Removed buf in fi_close when file is 0, 1 , 2

 090831		last modif
-------------------------------------------------------------*/

#define PM_LEVEL LEVEL_FI  	/* The Monitoring level for this file */

#define	DEBUG		0	/* Debugging option	*/

#include <midas_def.h>
#include <osfile.h>
#include <macrogen.h>
#include <trace.h>
#include <levels.h>
#include <error.h>
#include <ok.h>
#include <string.h>


extern int  eh_ed_i(), eh_ed_as(), eh_put1();
extern int pm_enter(), pm_ed_trace(), pm_iexit(), pm_trace();
extern int mm_free();

int fi_open(), fi_read(), fi_close();


#define _NFILES_	32	/* Maximal number of Files */

static char opened_mode[_NFILES_] = {0,0,0,0};
static char *filenames [_NFILES_] = {"stdin", "stdout", "stderr"};
static int mode;


#define set_mode(f)	mode = (f>=0 && f < _NFILES_ ? opened_mode[f] : 0);
#define FINISH		goto FIN

/*======================================================================*/
static char *save (name)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Copy the filename to a new piece of memory
.RETURNS Address of name
-------------------------------------------------------------*/
        char	*name;	/* IN: File name             */
{
	char	*p, *mm_alloc();
	int	l;

  l = strlen(name) + 1;
  if ((p = mm_alloc(l)))	oscopy(p, name, l);
  
  return(p);
}

/*======================================================================*/
static int fi_error (msg, fid)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Write error message.
.RETURNS 0
-------------------------------------------------------------*/
        char	*msg;	/* IN: Message associated */
        int	fid;	/* IN: File Identification */
{
  if ( (fid < 0) || (fid > _NFILES_) || (!filenames[fid]))
  	ERR_ED_I(msg, fid);
  else	ERR_ED_STRING(msg, filenames[fid]);
  
  return(0);
}

/*======================================================================*/
int fi_wopen (name)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Open file for special output mode (Variable)
.RETURNS File number, or NOK if error
.REMARKS Mode is variable-length, max = 2048 bytes.
	Useless on Unix.
-----------*/
        char	*name;	/* IN: File name             */
{
  osfop('V', 2048);
  return(fi_open(name,WRITE|RECORD_MODE));
}

/*======================================================================*/
int fi_open (name, mode)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Open file for specified access mode.
.RETURNS File number, or NOK if error
.REMARKS Mode may include the optional parameter RECORD_MODE.
	generating variable-length files on VMS.
-----------*/
        char	*name;	/* IN: File name             */
        int    	mode;	/* IN: Access mode 0, 1 or 2 */
{
	static char trace[]	  = "Open file[I ] ";
	static char mop[4]	  = {'I', 'O', 'M', 'A'};
	int	status, rec_mode;
	char	*phname;

  ENTER("fi_open");

  status = NOK;
  
  if (name)
  {	trace[10] = mop[mode&3];
  	rec_mode = mode & RECORD_MODE;
  	trace[11] = (rec_mode ? 'R' : ' ');
	TRACE_ED_STRING(trace,name);
	status = mode & ~RECORD_MODE;
	phname = osftr(name);		/* Physical Name */
	status = (rec_mode ? 	osaopen(phname, status) :
				osdopen(phname, status) );
	if (status < 0)	ERR_ED_STRING(osmsg(), name), status = NOK;
	else if (status < _NFILES_)
		opened_mode[status] = (mode & RECORD_MODE ? 1 : 0),
		filenames[status] = save(name);
  }
  else	ERROR("Invalid File Name (NULL)");

  EXIT(status);
}

/*======================================================================*/
long fi_seek (file, offset, direction)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Set file position.
.RETURNS New byte position, or EOF (impossible)
.REMARKS See osdseek.
-------------------------------------------------------------*/
        int     file;         /* IN: File number          */
        long 	offset;  /* IN: byte offset          */
        int     direction;    /* IN: FILE_START, FILE_CURRENT, FILE_END */
{
  	register long pos;

  ENTER(".fi_seek");

  set_mode(file);

  pos = (   mode ? 	osaseek(file,offset,direction) :
			osdseek(file,offset,direction) );

  if (pos == -1)	fi_error(osmsg(), file);

  EXITl ( pos );
}

/*======================================================================*/
int fi_load (name, offset, buf, nbytes)
/*++++++++++
.PURPOSE Load the ascii file `name', starting at position `offset',
	up to nbytes.
.RETURNS Actual number of bytes read (0 for error)
.REMARKS The most efficient way to load the file is used.
-----------*/
        char	*name;	/* IN: File name             */
        long	offset;	/* IN: Where to start the reading	*/
        char	*buf;	/* OUT: Buffer to get read bytes 	*/
        unsigned int nbytes;	/* IN: Size of buffer	*/
{
	int	status, fid;
	char	*p, *pe;

  ENTER("+fi_load");

  p = buf, pe = buf + nbytes;
  *p = EOS;
  status = osfunix();
  
  if_not(fid = fi_open(name, (status ? READ : READ|RECORD_MODE)))
  	FINISH;
  fi_seek(fid, offset, FILE_START);
  
  if (status)	/* Unix file */
  {  	status = fi_read(fid, p, pe-p);
  	if (status > 0)	p += status;
  }
  else		/* Use Record Mode	*/
  {	while (p < pe)
  	{	status = osaread(fid, p, pe-p);
  		if (status < 0)	break;
  		p += status;
	  	if (p < pe)	*(p++) = '\n';
  	}
	if (p > pe)	p = pe;
  }
  status = p - buf;  
  if (status < nbytes)	*p = EOS;
  fi_close(fid);

  FIN:
  EXIT(status);
}

/*======================================================================*/
int fi_close (file)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Close file.
.RETURNS NOK if failure, OK otherwise
.REMARKS See close.
-------------------------------------------------------------*/
        int     file;                   /* IN: File number          */
{
	int	status;

  ENTER("fi_close");

  set_mode(file);
  status = ( mode ? osaclose(file) : osdclose(file));

  if (status<0) 	fi_error(osmsg(), file), status = NOK;
  else if (file > 2)	mm_free(filenames[file]), status = OK,
  			filenames[file] = NULL_PTR(char);

  EXIT(status);
}

/*======================================================================*/
char *fi_name (file)
/*++++++++++++++++
.PURPOSE Retrieve name of an opened file
.RETURNS Address of name (NULL if not opened)
.REMARKS Not traced.
------------------*/
        int     file;                   /* IN: File number          */
{
	char	*p;

  if ((file >= 0) && (file < _NFILES_))
  	p = filenames[file];
  else	p = NULL_PTR(char);

  return(p);
}

/*======================================================================*/
int fi_read (file, record, nbytes)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Read up to nbytes from a file.
.RETURNS Actual number of bytes read, or EOF / NOK (error)
.REMARKS File must have been opened without RECORD_MODE option.
-------------------------------------------------------------*/
        int     file;                   /* IN: File number          */
        char    *record;                /* OUT: record buffer        */
        unsigned int     nbytes;    	/* IN: size of buffer       */
{
  	int  	lrec;

  ENTER("+fi_read");

  set_mode(file);

  lrec = NOK;
  
  if(mode)			/* Was opened with RECORD_MODE */
  	lrec = osaread(file, record, nbytes);
  else
  	lrec = osdread(file, record, nbytes);
  if ((lrec == -1) && (*osmsg()))
		fi_error(osmsg(), file);

  EXIT (lrec);
}

/*======================================================================*/
int fi_write (file, record, nbytes)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Write nbytes to file.
.RETURNS Number of bytes actually written, or NOK for errors
.REMARKS 
-------------------------------------------------------------*/
        int     file;                   /* IN: File number          */
        char    *record;                /* IN: record buffer        */
        unsigned int     nbytes;  	/* IN: size of buffer       */
{
	int	status;
	
  ENTER("+fi_write");

  set_mode(file);
  
  status = (mode ? 	osawrite(file,record,nbytes) :
  			osdwrite(file,record,nbytes) );

  if (status<0) 	fi_error(osmsg(), file), status = NOK;
  else			status = OK;

  EXIT (status);
}

/*======================================================================*/
long fi_tell (file)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Tells the current file position
.RETURNS Byte position, or EOF (impossible)
.REMARKS 
-------------------------------------------------------------*/
        int     file;                   /* IN: File number          */
{
  return(fi_seek(file, 0L, FILE_CURRENT));
}

/*======================================================================*/
int fi_gets (file, record, nbytes)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Read a record from a file, with insertion of EOS
.RETURNS OK if buffer large enough, EOF (end of file),
        NOK if record larger than nbytes or an error occured.
.REMARKS Record is always terminated by EOS; if record is larger
	than nbytes, the remainder is ignored.
	The file muast have been opened with RECORD_MODE option.
-------------------------------------------------------------*/
        int     file;                   /* IN: File number          */
        char    *record;                /* OUT: record buffer        */
        unsigned int     nbytes;        /* IN: size of buffer       */
{       
	register int  lrec;

  ENTER("fi_gets");

  set_mode(file);	
  lrec = NOK;
  if ((!mode) && file)			/* Not an error for stdin	*/
  	fi_error("File was not opened with RECORD_MODE: ", file);
  else
  {	lrec = osaread(file, record, nbytes);
	if (lrec >= 0)
	{	lrec = (lrec >= nbytes ? NOK : OK);
		TRACE(record);
	}
	else if (*osmsg())
	    	lrec = NOK,
  		fi_error(osmsg(), file);
  }	

  EXIT(lrec);
}

/*======================================================================*/
int fi_put (file, record)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Write an EOS-terminated string (without adding EOL indicator)
.RETURNS OK if all bytes written, 
         NOK if only a part of record was written or an error occured.
.REMARKS The string must be terminated with EOL followed by EOS for
         writing a complete record (or use fi_puts function)
-------------------------------------------------------------*/
        int     file;                   /* IN: File number          */
        char    *record;                /* IN: EOS-terminated record buffer */
{
	int 	length, status;
	
  ENTER("fi_put");
  TRACE(record);

  length = strlen(record);
  status = ( fi_write(file,record,length) == length ? OK : NOK);

  EXIT (status);
}

/*======================================================================*/
int fi_puts (file, record)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Write up an EOS-terminated string as a new record
.RETURNS OK if all bytes written, NOK if part of record is written
         or an error occured.
.REMARKS 
-------------------------------------------------------------*/
        int     file;                   /* IN: File number          */
        char    *record;                /* IN: EOS-terminated record buffer */
{
	int	status;

  ENTER("fi_puts");
  TRACE(record);

  set_mode(file);

  status = ( mode ? osaput(file,record) : osdputs(file,record));
  if (status < 0)	fi_error(osmsg(), file), status = NOK;
  else			status = OK;

  EXIT (status);
}

/*======================================================================*/
int fi_flush (file)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Flush (ensure that buffers are copied to disk)
.RETURNS OK / NOK
.REMARKS 
-------------------------------------------------------------*/
        int     file;                   /* IN: File number          */
{
	int	status;
	
  ENTER("fi_flush");

  set_mode(file);
  status = ( mode ? osaflush(file) : osdwait());
  if (status < 0)	fi_error(osmsg(), file), status = NOK;
  else			status = OK;

  EXIT (status);
}

