/*===========================================================================
  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 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
.LANGUAGE    C
.AUTHOR      IPG-ESO Garching
.CATEGORY    Host operating system interfaces. Data file i/o.
.COMMENTS    
             Handling of data files. These routines access data in binary
             format. Data files are accessed via direct i/o
             in a synchronous way.
             Files are referenced by the physical filename 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.
             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. On read on end of file, the value of -1 is
	     also returned.
.VERSION  0.0 	25-Aug-1986   Definition     J.D. Ponz
.VERSION  1.0 	28-Nov-1986   Programmation  B.   Pirenne
.VERSION  1.1 	14-Apr-1987   Upgrade performance B. Pirenne
.VERSION  1.2 	15-Jul-1987   Include asynchronous file access B. Pirenne
.VERSION  1.3 	07-Dec-1987   Upgrade performance and compact the code 
				 I. Suisalu
.VERSION  1.4 	31-May-1988   Cosmetic changes. C. Guirao.
.VERSION  1.5 	04-Oct-1988   osdopen creates the file if non-existant.
.VERSION  1.6 	21-Jun-1989   O_TRUNC added at open in WRITE mode.
				and open (path, flags, -1) now.
.VERSION  1.7 	07-Nov-1989   Simplified osdopen
.VERSION  1.8	05-Feb-1990   Default creat mode is 0644 (no executre right)
.VERSION  1.9	14-Mar-1990   Removed bug is osdread (set errno to 0)
.VERSION  1.10	19-Dec-1990   Not checking FNAME_LEN any more CG.
.VERSION  1.11	21-Oct-1991   fsync() removed. Not portable. CG.
.VERSION  2.1	29-May-1992   open creates the file with default umask 0666.CG
.VERSION  3.1	09-Mar-1994   Decompressing in the fly. CG.

 090325		last modif

------------------------------------------------------------*/
/*
 * Define _POSIX_SOURCE to indicate
 * that this is a POSIX program
 */
#define _POSIX_SOURCE 1

#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <osparms.h>
#include <proto_os.h>           /* ANSI-C prototyping */

#define MAX_TABLE_ENTRY 20
static struct DECOMPRESS
   {
   int entry;
   int longest_suffix;
   char *suffix[MAX_TABLE_ENTRY + 2];
   char *command[MAX_TABLE_ENTRY + 2];
   } decompress =
{
2, 2,
{
".Z", ".z"},
{
"zcat", "gunzip -c"},};

static int decomp_table_read = 0;
static FILE *file_ptr[_NFILE_];
extern FILE *popen ();		/* Not defined in <stdio.h> of OSF/1 */
extern int pclose();


#ifdef O_TRUNC
#   define O_write		(O_WRONLY | O_TRUNC | O_CREAT)
#else
#   define O_write		(O_WRONLY | O_CREAT)
#endif
#define OSDMASK		0666


/*
 * read_decomp_table() " reads the file containing this table from the
 * environment $DECOMPRESS_TABLE, otherwise uses $MID_INCLUDE/decompress.dat
 * 
 * The syntax of this file must be followed strictly:
 *  - Only '#' as the first character makes a line in comments.
 *  - Separators are blanks or tabs.
 *  - Suffixes are in the first column and without quotes.
 *  - Commands are in the second column. Double quotes are required if the
 *    command contains blanks.
 *  - Commands can contain the character '*' that represents the file to be
 *    decompressed. NOTE: only the first '*' is expanded.
 *  - Any thing after the second column is ignored.
 *  - Incomplete entries are ignored (i.g. only the suffix)
 *  - Only the first MAX_TABLE_ENTRY (20) entries are read, the rest is ignored.
 */
static void read_decomp_table ()

{
char *getenv ();
static char *decomp_table_file = NULL;
FILE *fd, *fopen ();
int i, suffix_length;
char *q, *mid_include;
char buf[100], suffix[8], command[80];

  /* check if DECOMPRESS_TABLE environment is defined */
if ((decomp_table_file = getenv ("DECOMPRESS_TABLE")) == NULL)
   {
   if ((mid_include = getenv ("MID_INCLUDE")) == NULL)
      {
      /* Using the built-in table */
      /* printf("No DECOMPRESS_TABLE or MID_INCLUDE definition\n"); */
      return;
      }
   decomp_table_file =
   malloc ((size_t)(strlen (mid_include) + strlen ("/decompress.dat") + 1));
   strcpy (decomp_table_file, mid_include);
   strcat (decomp_table_file, "/decompress.dat");
   }

if ((fd = fopen (decomp_table_file, "r")) == NULL)
   {
   /* Using the built-in table */
   /* printf("Cannot open DECOMPRESS_TABLE %s\n",decomp_table_file); */
   return;
   }

  /* reads the contents of decompressed table file */
while (fgets (buf, sizeof (buf), fd) != NULL
       && decompress.entry < MAX_TABLE_ENTRY)
   {
   if (buf[0] == '#')
   continue;
   for (q = buf; *q && ((*q == ' ') || (*q == '\t')); q++);
   for (i = 0; (*q != '\n') && (*q != ' ') && (*q != '\t'); q++)
   suffix[i++] = *q;
   if (i)
   suffix[i] = '\0';
   else
   continue;
   for (; *q && ((*q == ' ') || (*q == '\t')); q++);
   if (*q == '"')
      {
      q++;
      for (i = 0; (*q != '\n') && (*q != '"'); q++)
      command[i++] = *q;
      }
   else
      {
      for (i = 0; (*q != '\n') && (*q != ' ') && (*q != '\t'); q++)
      command[i++] = *q;
      }
   if (i)
   command[i] = '\0';
   else
   continue;
   suffix_length = strlen (suffix);
   decompress.suffix[decompress.entry] = malloc ((size_t)(suffix_length + 1));
   decompress.command[decompress.entry] = malloc ((size_t)(strlen (command) + 1));
   strcpy (decompress.suffix[decompress.entry], suffix);
   strcpy (decompress.command[decompress.entry], command);
   if (suffix_length > decompress.longest_suffix)
   decompress.longest_suffix = suffix_length;
   decompress.entry++;
   }
}

static int match_preffix (phname)
char *phname;
{
int i;
int n_suffix;

for (i = decompress.entry - 1; i >= 0; i--)
   {
   n_suffix = strlen (decompress.suffix[i]);
   if (!strcmp
       (&phname[strlen (phname) - n_suffix], decompress.suffix[i]))
   return (i);
   }
return (-1);
}


static int match_compressed_file (phname, comp_phname)
char *phname;
char **comp_phname;
{
struct stat buf;
int i;

*comp_phname = malloc ((size_t)(strlen (phname) + decompress.longest_suffix + 1));
for (i = decompress.entry - 1; i >= 0; i--)
   {
   strcpy (*comp_phname, phname);
   strcat (*comp_phname, decompress.suffix[i]);
   if (stat (*comp_phname, &buf) == 0 && S_ISREG (buf.st_mode))
   return (i);
   }
free (*comp_phname);
return (-1);
}

static char *get_command (phname, match)
char *phname;
int match;
{
char *command;
char *ptr_to_star;

command =
calloc (strlen (decompress.command[match]) + strlen (phname) + 2, 1);

if ((ptr_to_star = strchr (decompress.command[match], '*')) == NULL)
   {
   strcpy (command, decompress.command[match]);
   strcat (command, " ");
   strcat (command, phname);
   }
else
   {
   strncpy (command, decompress.command[match],
            (int) (ptr_to_star - decompress.command[match]));
   ptr_to_star++;
   strcat (command, phname);
   strcat (command, ptr_to_star);
   }
return (command);
}

int osdopen (phname, mode)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Open EXISTING binary data file for read or write. The variable mode
         defines the way of opening the file as :
         READ, WRITE, READ_WRITE and APPEND.
         The file pointer is set to the beginning of the file.
.RETURNS Upon successful completion a positive number with the 
.RETURNS file identification is returned. (-1 otherwise).
.REMARKS System dependencies:
 -- UNIX: open(2)
------------------------------------------------------------*/
char *phname;                                    /* physical filename */
int mode;                                        /* open mode */

{
int fid, t;
int myerrno;
int match;
FILE *fpin;
char *command;
char *compressed_file;


switch (mode & 3)
   {
   case READ:                  /* open for read only */
   t = O_RDONLY;
   /*
    * Read the decompression table first 
    */
   if (!decomp_table_read)
      {
      decomp_table_read++;
      read_decomp_table ();
      }
   /* 
    * Check if filename contains a suffix  
    */
   if ((match = match_preffix (phname)) != -1)
   command = get_command (phname, match);
   /* 
    * Otherwise try to open filename 
    */
   else if ((fid = open (phname, t, OSDMASK)) > 0)
   return (fid);
   /* 
    * Otherwise try to find filename + suffix 
    */
   else
      {
      myerrno = errno;
      if ((match =
           match_compressed_file (phname, &compressed_file)) != -1)
         {
         command = get_command (compressed_file, match);
         free (compressed_file);
         }
      /* 
       * Otherwise return the error returned by open(phname) 
       */
      else
         {
         oserror = myerrno;
         return (-1);
         }
      }

   /* 
    * Open decompress command as a pipe 
    */
   printf ("%s\n", command);
   if ((fpin = popen (command, "r")) == NULL)
      {
      free (command);
      oserror = errno;
      return (-1);
      }
   free (command);
   fid = fileno (fpin);
   if (fid >= _NFILE_)
      {
      oserror = EINVAL;
      return (-2);
      }
   file_ptr[fid] = fpin;
   return (fid);
   break;
   case WRITE:                 /* open for write only */
   t = O_write;
   break;
   case READ_WRITE:            /* open for read-write */
   t = O_RDWR;
   break;
   case APPEND:                /* open for append */
   t = O_APPEND | O_WRONLY;
   break;
   default:
   t = O_RDWR;
   }

if ((fid = open (phname, t, OSDMASK)) == -1)
   oserror = errno;

return (fid);
}

int osdclose (fid)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Closes data file. The argument fid is the file identification
         obtained from osdopen.
.RETURNS Value 0 on normal return. (-1 otherwise).
.REMARKS System dependencies:
 -- UNIX: close(2)
------------------------------------------------------------*/
int fid;                                         /* file identification */

{
FILE *fd;


if (fid < 3)
   {			/* fid should not be stdin, stdout, stderr */
   return (-1);
   }

if (fid >= _NFILE_ || ((fd = file_ptr[fid]) == NULL))
   {
   if (close (fid) == -1)
      {
      oserror = errno;
      return (-1);
      }
   }
else
   {
   file_ptr[fid] = NULL;
   if (pclose (fd) == -1)
      {
      oserror = errno;
      return (-1);
      }
   }
return (0);
}

int osdread (fid, pbuf, nobyt)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Synchronous read from a data file. 
         The argument fid is the file identification
         obtained from osdopen. osdread reads nobyt bytes from the current
         file position and stores the information into the buffer
         pointed by pbuf. On devices capable of seeking, the read starts at the
         position of the file pointer associated with fid.
         Upon return the pointer is incremented by the number of bytes actually
         read.
.RETURNS Number of bytes actually read. (-1 in case of failure or end of file).
.REMARKS System dependencies:
 -- UNIX: read(2)
------------------------------------------------------------*/
int fid;                     /* file identification */
char *pbuf;                  /* pointer to input buffer */
size_t nobyt;                /* number of input bytes */

{
int nread;



errno = 0;                      /* must be initialized... */

nread = (int) read (fid, (void *) pbuf, nobyt);
if (nread > 0) return (nread); 

oserror = errno;                /* error in reading file or at EOF */
return (-1);
}


int osdofread (fid, address, pbuf, nobyt)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE move file pointer to address + read from there
         The argument fid is the file identification
         obtained from osdopen. The address defines
         the pointer position in bytes relative to the
         start of the file
.RETURNS no. of bytes read
.REMARKS System dependencies:
 -- UNIX: lseek(2)
------------------------------------------------------------*/
int fid;                                         /* file identification */
off_t address;                                    /* file pointer */
char *pbuf;                                      /* pointer to input buffer */
size_t nobyt;                                  /* number of input bytes */

{

if (lseek (fid, address, SEEK_SET) != (off_t)-1)
   return ((int) read (fid, (void *) pbuf, nobyt));
else
   return (-1);
}


int osdwrite (fid, pbuf, nobyt)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Synchronous write into a data file.
         The argument fid is the file identification
         obtained from osdopen. osdwrite writes nobyt bytes from the buffer
         pointed by pbuf. On devices capable of seeking, the write starts at
         the position of the file pointer associated with fid.
         Upon return the pointer is incremented by the number of bytes actually
         written. 
.RETURNS Number of characters actually written. (-1 in case of failure).
.REMARKS System dependencies:
 -- UNIX: write(2)
------------------------------------------------------------*/
int fid;                  /* file identification */
char *pbuf;               /* pointer to output buffer */
size_t nobyt;             /* number of output bytes */

{
int i;

if ((i = (int) write (fid, (void *) pbuf, nobyt)) == -1)
   oserror = errno;

return (i);
}


int osdofwrite (fid, address, pbuf, nobyt)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE move file pointer to address + write to there
         The argument fid is the file identification
         obtained from osdopen. The address defines
         the pointer position in bytes relative to the
         start of the file
.RETURNS no. of bytes read
.REMARKS System dependencies:
 -- UNIX: lseek(2)
------------------------------------------------------------*/
int fid;                    /* file identification */
off_t address;              /* file pointer */
char *pbuf;                 /* pointer to input buffer */
size_t nobyt;               /* number of input bytes */

{

if (lseek (fid, address, SEEK_SET) != (off_t)-1)
   return ((int) write (fid, (void *) pbuf, nobyt));
else
   return (-1);
}


off_t osdseek (fid, address, mode)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE moves the file pointer.
         The argument fid is the file identification
         obtained from osdopen. The address defines
         the pointer position in bytes 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).
         (see midas/osparms.h). (i.e. : an address=0 and OS_START set means
	 first byte of the file).
.RETURNS Upon successful completion, the resulting pointer position
.RETURNS measured in bytes is returned, in case of error -1L.
.REMARKS System dependencies:
 -- UNIX: lseek(2)
------------------------------------------------------------*/
int fid;                                         /* file identification */
off_t address;                                   /* file pointer */
int mode;                                        /* mode of addressing */
{
off_t offret;

offret = lseek (fid, address, mode);
if (offret == (off_t)-1) oserror = errno;

return (offret);
}

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;
int i;

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

if ((i = osdwrite (fid, pbuf,(size_t) i)) == -1)
   oserror = errno;

*p = '\0';
return (i);
}

int osdwait ( /*fid */ )
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Waits for asynchronous input/output to be completed.
         The argument fid is the file identification
         obtained from osdopen. If the process is waiting for
         input/output operation, the execution of the process is suspended
         until the transfer is complete.
         !!! NOT IMPLEMENTED : RETURNS ALWAYS 0 !!!
.RETURNS The function returns 0 for success, and -1 if any error was
	 detected.
.REMARKS System dependencies:
 -- UNIX: sync(2)
------------------------------------------------------------*/
                                /*int fid;*//* file identification */
{
return (0);
}
