/*===========================================================================
  Copyright (C) 1995-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        osa
.LANGUAGE    C
.AUTHOR      IPG-ESO Garching
.CATEGORY    Host operating system interfaces. Files in ASCII (text files)
.COMMENTS    
             Handling of ascii files (using buffered I/O). These 
             routines convert text data from the host format into 
             internal format and viceversa. 
             Text files are accessed sequentially in a synchronous form.
             Text files are referenced by the physical name in the 
             open function. A file identifier is returned by this 
             function to be used in later i/o operations.
             Associated to each file there is a pointer to locate the
             current file position (hidden in the file descriptor).
             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.
             Symbolic error codes for each function to be defined.

.VERSION  0.0 	25-Aug-1986   Definition     J. D. Ponz
.VERSION  1.0 	04-Nov-1986   Programmation  B. Pirenne
.VERSION  1.1 	14-Apr-1987   Improve performance B. Pirenne
.VERSION  1.5 	20-Jul-1987   ost* -> osa* ; use "fileno()" to get the file
                                 #in osaopen() ; upgrade osaread(). B. Pirenne.
.VERSION  1.6 	31-May-1988   Cosmetic changes. C. Guirao
.VERSION  1.7 	04-Oct-1988   osaread modified: ensure the rest of a too long
				record is ignored (F. Ochsenbein)
.VERSION  1.8 	26-Jun-1989   Modified osaread  (was detroying last byte)
.VERSION  1.9 	07-Nov-1989   Simplified osaopen
.VERSION  1.10	08-Feb-1990   Corrected bug in osaread (error reported)
.VERSION  1.11	04-May-1990   Be sure that stdin (0), stdout(1) ansd stderr(2)
				are understood...
.VERSION  1.12	19-Dec-1990   Not checking FNAME_LEN any more CG.

 090324		last modif
------------------------------------------------------------*/

#include <fcntl.h>
#include   <stdio.h>
#include   <errno.h>
#include   <osparms.h>
#include   <proto_os.h>		/* ANSI-C prototyping */

/* array to contain up to _NFILE_ file pointers. 
 * This will be used to allow a the use of a transparent and     
 * integer file identifier                     
 */
static FILE *fptr[_NFILE_];

#ifdef __STDC__
static FILE *get(int fid)
#else
static FILE *get(fid)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Convert fileno into file pointer.
.RETURNS File pointer. NULL if error.
.REMARKS Ensures a correct check.
------------------------------------------------------------*/
	int   fid;	/* IN : file identifier */
#endif
{
	FILE *fd;

  fd = NULL;

  oserror = 0;

  if (fid >= _NFILE_) 	oserror = EINVAL;
  else	{
    switch(fid){
    case 0:
      fd = stdin;
      break;
    case 1:
      fd = stdout;
      break;
    case 2:
      fd = stderr;
      break;
    default:
      fd = fptr[fid];
    }
    if (fd == NULL) oserror = EINVAL;
  }

  return(fd);
}

int osaopen(phname, mode)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Opens text file for read or write.
         The variable mode
         defines the way of opening the file as READ,
         WRITE, READ_WRITE and APPEND. (see midas/osparms.h).
         The file pointer is set to the beginning of the file.
.RETURNS Upon successful completion a file descriptor is returned. -1 is
         returned on error.
.REMARKS System dependencies:
 -- UNIX: fopen(3S), fileno(macro in stdio.h)
------------------------------------------------------------*/
char *phname;			/* IN : physical filename */
int mode;			/* IN : open mode */
{
	FILE *fid, *fopen();
	static char *fmode[4] = { "r", "w", "r+", "a" };
	char *t;
	int ret;


#if 0	/* Old code */
	switch (mode) {
	case READ:                 /* open for read only */
		t = fmode[0];
		break;
	case WRITE:                /* open for write only */
		t = fmode[1];
		break;
	case READ_WRITE:           /* open for read-write */
		t = fmode[2];
		break;
	case APPEND:               /* open for append */
		t = fmode[3];
		break;
	default:
		oserror = EINVAL;
		return(-1);
	}
#else	/* New Code */
 	t = fmode[mode&3];
#endif

	if ((fid = fopen(phname,t)) == NULL) {
		oserror = errno; 
		return(-1);
		}
	ret = fileno(fid);	/* indice of the file table is  */
	if (ret >= _NFILE_) {
		oserror = EINVAL;
		return(-2);
		}
	fptr[ret] = fid;	/* the _file of struct FILE and */
				/* this number becomes the file ptr */
	return(ret);
}

int osaclose(fid)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Closes a text file.
         The argument fid is the file identification obtained from osaopen.
.RETURNS Value 0 on normal return, -1 on error. 
.REMARKS System dependencies:
 -- VAX VMS: 
 -- UNIX: fclose(3S)
 -- MIDAS VMS Equivalence: Not available.
------------------------------------------------------------*/
int   fid;			/* IN : file identifier */
{
	FILE *fd;
	int ret;
    
	if ( (fd = get(fid)) == NULL) return(-1);

	if ((ret = fclose(fd)) < 0) oserror = errno;
	fptr[fid] = NULL;	/* File Closed: reset File Pointer */
	return(ret);
}

int osaread(fid, pbuf, nochar)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Synchronous read from text file.
         The argument fid is the file identification obtained from osaopen.
         The routine reads 1 record from the current file
         position and stores up to nochar characters into the buffer 
         pointed by pbuf. It the record is larger than the buffer size,
         the remainder is ignored.
	 The carriage return character is never returned. The buffer returned
	 is always null terminated.
.RETURNS Number of characters of the record. (may be less than nochar 
	 if the EOF or a carriage return is encountered, larger if record 
	 truncated).
         Returns 0 if the record only contained the line feed char.
         Returns -1 if an error is encountered or if the end of file is
	 reached. (In these last cases, oserror is set to the relevant error
	 number).
.REMARKS System dependencies: fgets / fgetc
------------------------------------------------------------*/
	int	fid;			/* IN : file identifier */
	char	*pbuf;			/* IN : pointer to i/o buffer */
	int	nochar; 		/* IN : number of i/o characters */
{
	FILE 	*fd;
	char 	*pc, *pe;
	int  	c;

	if ( (fd = get(fid)) == NULL) return(-1);

	errno = 0;			/* MUST BE INITIALIZED... Feb-1990 */
	if (fgets(pbuf, nochar, fd) == NULL) {
		oserror = errno;
		return(-1);
		}

	pe = pbuf + nochar - 1;		/* Last Available Byte */
	
    /* Continue to read if the end-of-record not found */

	for (pc = pbuf; (pc <= pe) && (*pc != '\n') && (*pc != '\0'); pc++ ) ;
	
	if (pc > pe)	/* Not Found */
		*pe = '\0', pc = pe;

	if (*pc == '\n')	/* Replace the newline + carriage-return */
	{   
		while ( (--pc >= pbuf) && (*pc == '\r')) ;
		*++pc = '\0';
	}
	else	for (c = 0, --pc; (c != EOF) && (c != '\n'); pc++) 
		c = getc(fd);

	return((pc - pbuf));
}

int osawrite(fid, pbuf, nochar)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Synchronous RECORD write into a text file.
         The argument fid is the file identification obtained from osaopen.
         The routine writes nochar characters from the buffer pointed
         by pbuf into the current file position.
         Upon return the file pointer is incremented by the number of 
         characters actually written.
.RETURNS Number of characters actually written. (possibly 0 on error)
.REMARKS System dependencies:
 -- UNIX: fwrite(3S)
------------------------------------------------------------*/
int  fid;			/* IN : file identifier */
char *pbuf;			/* IN : pointer to i/o buffer */
int nochar;	        	/* IN : number of i/o characters */
{
	FILE 	*fd;
	int 	ret, add_newline;

	if ( (fd = get(fid)) == NULL) return(-1);

	add_newline = 0;
	ret = 0;

	if (nochar)		/* There are bytes to write... */
	{
		ret = fwrite(pbuf, sizeof(char), nochar, fd);
		if (ret == 0)	;	/* Error */
		else if (*(pbuf+nochar-1) != '\n')	add_newline = 1;
	}
	else	add_newline = 1;

	if (add_newline)	ret += fwrite("\n", 1, 1, fd);

	if (ret == 0)		oserror = errno;

	return(ret);
}

long osaseek(fid, address, mode)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE moves read/write pointer to text file. The address defines
         the pointer position in characters relative to:
         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 osaopen.
.RETURNS Upon successful completion, the resulting pointer position
.RETURNS measured in characters is returned or -1 on error.
.REMARKS System dependencies:
 -- UNIX: fseek(3S), ftell(3S)
------------------------------------------------------------*/
int  fid;			/* IN : file identifier */
long address;			/* IN : file pointer */
int  mode;			/* IN : mode of addressing */
{
	long ftell();
	FILE 	*fd;

	if ( (fd = get(fid)) == NULL) return(-1L);

	if (fseek(fd, address, mode) < 0) {
		oserror = errno;
		return(-1L);
		}
	return(ftell(fd));
}

int osawait(fid)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Flushes the input/output streams.
.RETURNS The function returns 0 for success, and -1 if any error was detected
         (such as trying to write to a file that has not been opened for 
         writing) was detected.
.REMARKS System dependencies:
 --UNIX: fflush(3)
------------------------------------------------------------*/
	int fid;			/* file identification */
{
	FILE 	*fd;

	if ( (fd = get(fid)) == NULL) return(-1);

	return(fflush(fd));
}

long osasize(fid)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Retrieve the current size of an opened file.
.RETURNS The size of the file, -1 in case of error (oserror explains)
.REMARKS 
------------------------------------------------------------*/
	int fid;			/* IN: file identification */
{
	FILE 	*fd;
	long ret, pos, ftell();

    if ( (fd = get(fid)) == NULL) return(-1L);


    pos = ftell(fd);		/* Current position 	*/
    ret = -1;
	
    if (pos != -1)
    {	(void)fseek(fd, 0L, 2);	/* end of file		*/
	ret = ftell(fd);	
	(void)fseek(fd, pos, 0);
    }
    else oserror = errno;

    return(ret);
}

int osaunix(/*fid*/)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Tells if a file is Unix-like
.RETURNS Always 1
.REMARKS For VMS compatibility
------------------------------------------------------------*/
/*int fid;*/			/* IN: file identification */
{
    return(1);
}

