/*===========================================================================
  Copyright (C) 1988-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        osfparse.c
.LANGUAGE    C
.AUTHOR      IPG-ESO Garching
.ENVIRONMENT	Unix Version
.CATEGORY    Host operating system interfaces. File management.
.COMMENTS    
	 File name Parsing.
	 A file name can be decomposed into path, filename,
	 extension and version. VMS, Unix or MSDOS file naming
	 conventions are recognized.
	 
	
\begin{TeX}
	 In particular, the VMS convention of the colon ({\bf:})
	 representing a path is accepted, even with multiple pathnames.
	The following example shows how the translation works with osftr:
	Let's assume that the environment variable {\em myPath} is defined as
	\fbox{{\tt .,\$home,/tmp}}. The file named {\tt myPath:test.log}
	will be looked for, first in the current directory ({\tt .}),
	if not found in the {\tt \$home} directory, and if still not found
	in the {\tt /tmp} directory. 
\end{TeX}


.VERSION 1.0 	05-Feb-1988   F. Ochsenbein. 
.VERSION 1.1 	05-Apr-1989   Don't convert to uppercase in Unix...
.VERSION 1.2 	19-Jun-1989   Convention of Logical Names followed by :
				(to convert names from VMS)
.VERSION 1.3 	05-Dec-1989   Names followed by : may have several paths,
				separated by commas.
.VERSION 1.4 	31-Jan-1990   Removed bug in osfsupply - Cosmetics
.VERSION 1.5 	23-Nov-1990   Don't crash if NULL filename

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

#include <filedef.h>
#include <atype.h>
#include <macrogen.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <proto_os.h>		/* ANSI-C prototyping */

#ifndef F_OK
#define F_OK	0
#endif

#define COMPONENTS 	(__PATH_+1)
static short int comp1[COMPONENTS][2] = { {0,0}}; 
static short int comp2[COMPONENTS][2] = { {0,0}}; 

static char *buffer = NULL_PTR(char);
static int  lbuffer = 0;
					/* Index + length of components */

/*===========================================================================*/
#ifdef __STDC__
static void strlower (char * str )
#else
static void strlower ( str )
/*+++++++++
.PURPOSE  Converts (in place) a string to lower case
.RETURNS  Nothing
--------*/
     char *str;	/* MOD: starting address        */
#endif
{
	register char *p;

  for (p = str; *p; p++)
	*p = tolower(*p); 

}

/*==========================================================================*/
#ifdef __STDC__
static int split(char * name , short comp[6][2])
#else
static int split(name, comp)
/*+++
.PURPOSE Split a file name into components, returned in `comp'.
.RETURNS 0 (Unix) / 1 (VMS or MSDOS)
.REMARKS 
---*/
        char 	*name;	/* IN: EOS-terminated File Name	*/
	short int comp[COMPONENTS][2]; /* OUT: Index + length of components*/
#endif
{ 
  	register int i, len, status;

  oscfill((char *)comp, sizeof(comp1), 0);	/* initialize length and indexes */

  len = strlen(name);

	/* The structure of VMS file names is:
	   NODE::DEVICE:[DIRECTORY]FILE.EXTENSION;VERSION	*/

	/* Look for path name (terminated by / )	*/

  i = oscbloc(name, len, '/');		/* Unix--like	*/
  if (i < 0)	i = oscbloc(name, len, '\\'); /* Look MSDOS */
  if (i < 0)	i = oscbloc(name, len, ']') ; /* Look VMS */
  if (i < 0)	i = oscbloc(name, len, ':') ; /* Look VMS */

  status = 0;

  if (i >= 0)		/* A Path was found, reset name to remaining chars */
  {	status = *(name+i);
 	if ( (status == ']') || (status == '\\') ) status = 1;
	else	status = 0;

  	comp[__PATH_][1] = ++i;
  	name += i, len -= i;
	for (i = __PATH_; --i >= 0; )	/* Set Indexes	*/
			comp[i][0] = comp[__PATH_][1];
  }

	/* Look for version	*/

  i = oscbloc(name, len, ';');
  if (i >= 0)	/* Version Found */
  {	comp[__VERSION_][0] += i;	/* Offset */
	comp[__VERSION_][1] = len - i;
	len = i;
  }

	/* Look for extension	*/

  i = oscbloc(name, len, '.');
  if (i >= 0)	/* Extension Found */
  {	comp[__EXTENSION_][0] += i;
	comp[__EXTENSION_][1] = len - i;
	len = i;
  }

	/* Look for file name	(remaining bytes)	*/

  comp[__FILE_][1] = len;

  return(status);
}	

/*==========================================================================*/
#ifdef __STDC__
static char * path(char * name , int len)
#else
static char *path(name, len)
/*+++
.PURPOSE Convert a non-Unix (i.e. VMS or MSDOS) pathname
.RETURNS Pointer to EOS-terminated pathname if transformation needed
.REMARKS Unix-like $... are converted.
---*/
        char *name;		/* IN: Path Name	*/
        int  len;		/* IN: Length of Path Name	*/
#endif
{ 
	char	*p, *pe, *q, savec, dir_sep;
	char	*oshenv();
	int	i;
	char	the_path[256];
	static	char zero = EOS;
	static	char *pathname = NULL_PTR(char);
	static	int  lpathname = 0;

  if (len <= 0)		return(&zero);
  
  pe = name + len - 1;	/* Last character */
  dir_sep = *pe;
  savec = '\0';				/* right default value? */
  
  if ((dir_sep == '/') && (*name != '$'))
  	return(name);			/* No translation required */
  
  i = 0;				/* Index in the_path	*/
  if (*name == '$')	
  {	q = name + oscloc(name, len, dir_sep);
  	savec = *q, 	*q  = EOS;
	p = oshenv(name+1, NULL_PTR(char));
	if (p)
	{	while ( (*p) && (i < sizeof(the_path)-1))
			the_path[i++] = *(p++);
		name = q;
	}
	*q = savec;
  }
  for (p = name; (p <= pe) && (i < sizeof(the_path)-1); )
	the_path[i++] = *(p++);
  the_path[i] = EOS;

	/* The local variable the_path now contains the path name.
		Compute length, and 
		Change it if source is MSDOS or VMS
	 */

  if (dir_sep == ']')
  	for (p = the_path; *p; p++)
  		if (*p == '-')	i += 2;	/* VMS - must translate to ../ */

  if (i > lpathname)			/* Expand Buffer */
  	lpathname = (i+63)& ~63, pathname = osmmexp(pathname, lpathname);

	/* Copy with modifications */

  p = the_path, q = pathname;

  if (dir_sep == ']')	for( ; *p ; p++) switch(*p)
  { case '[':
	savec = 1;		/* The next dash has a special meaning	*/
	if (*(++p) == '.')	continue;
  	if (*(p--) != '-')	*(q++) = '/';
  	continue;
    case ']':
	savec = 1;		/* The next dash has a special meaning	*/
	*(q++) = '/';
	continue;
    case '-':
	if (savec)	
	{ 	savec = 0; 	/* The next dash is standard	*/
		*(q++) = '.', *(q++) = '.';	
		continue;
	}
    case '.':
	savec = 1;		/* The next dash has a special meaning	*/
	*(q++) = '/';		continue;
    default:
	savec = 0; 		/* The next dash is standard	*/
    	*(q++) = *p;		continue;
  }
  else  for( ; *p ; p++)
  {	*(q++) = (*p == '\\' ? '/' : *p);
  }
  *q = EOS;

  return(pathname);
}	

/*======================================================================*/
char *osfparse(name, option)
/*+++
.PURPOSE Extract from a file name the specified items, copy them
	to an internal buffer.
.RETURNS Address of the extracted item(s).
.REMARKS Options are _PATH_, _FILE_, _EXTENSION_, _VERSION_.
	Filenames are NOT converted (use osftr or osfsupply)
---*/
	char     *name;		/* IN: File name			*/
	unsigned int option;	/* IN: what to extract, as a \
				combination of options			*/
{ 
	char 	*p;
	int 	i, l;
	char	local_copy[256];
  
  if (!name)	name = "";	/* Take default name...	*/

  if (name == buffer)  {	/* Copy first... */
	l = strlen(name) + 1;
	l = MIN(l, sizeof(local_copy));
	oscopy(local_copy, name, l);
	name = local_copy;
  }
  split(name, comp1);		/* Split the filename into its components */


	/* Compute the required length	*/

  l = 1;				/* Take into account the EOS!	*/
  for (i = COMPONENTS; --i>=0; )
	if (option & (1<<i))		l += comp1[i][1];

  if (l > lbuffer)
  	lbuffer = (l+63)& ~63, buffer = osmmexp(buffer, lbuffer);


	/* Now, copy the required items	*/

  p = buffer;
  for (i=COMPONENTS; --i>=0; )
	if (option & (1<<i))
		p += oscopy(p, name + comp1[i][0], comp1[i][1]);
  *p = EOS;				/* Terminate with EOS	*/

  return(buffer);
}

/*======================================================================*/
char *osfsupply(name, template)
/*+++
.PURPOSE Convert a file name according to a specified template:
	missing parts in name are replaced by the corresponding parts 
	provided in template.
	The resulting name is copied (in uppercase on VMS)
	to an internal buffer.
.RETURNS The address of the internal buffer
---*/
	char     *name;		/* IN: File name			*/
	char 	 *template;	/* IN: Template name			*/
{ 
	char	*oshenv();
	char 	*p, *pname;
	int 	i, l, lpath, lp, in_path;
	char	local_copy[256], isvms, ismult, next_path;

	/* First, split the filenames into its components */

  if (!name)	name = "";	/* Take default name...	*/

  next_path = '\0';

  if (name == buffer)  {	/* Copy first... */
	l = strlen(name) + 1;
	l = MIN(l, sizeof(local_copy));
	oscopy(local_copy, name, l);
	name = local_copy;
  }
	
  isvms =  split(name, comp1);
  isvms |= split(template, comp2);

  if (comp1[__PATH_][1])	pname = name +     comp1[__PATH_][0],
					   lpath = comp1[__PATH_][1];
  else if (comp2[__PATH_][1])	pname = template + comp2[__PATH_][0],
					   lpath = comp2[__PATH_][1];
  else pname = NULL_PTR(char),	lpath = 0;
  
  ismult = 0;
  in_path = 0;
  if (pname)
  {	p = pname + lpath - 1;
	if (*p == ':')	ismult = *p, *p = EOS;
  }
  else	ismult = 0;

	/* The loop is executed more than once only when the pathname
	   is terminated with ':' and there are several pathnames
	   separated by commas
	*/

  while(1)
  {	if (ismult)	/* Several Names. */
  	{	if_not(p = oshenv(pname, NULL_PTR(char)))	p = pname;
  		p += in_path;
  		lp = oscloc(p, strlen(p), ',');
  		next_path = *(p+lp);
  		in_path += (lp+1);
  	}
	else		/* Translate Name */
	{	p = pname, 		lp = lpath;
  		if (p)	p = path(p, lp);
		if (p != pname) 	lp = strlen(p);
  	}

		/* Compute the required length	*/

  	l = lp + 2 ;		/* Take into account EOS and final / 	*/
  	for (i = __PATH_; --i>=0; )
  	{	if (comp1[i][1])	l += comp1[i][1];
		else			l += comp2[i][1];
	}
	if (l > lbuffer)
  		lbuffer = (l+63)& ~63, buffer = osmmexp(buffer, lbuffer);

		/* Now, copy the required items	*/

  	if (p)	
  	{	p = buffer + oscopy(buffer, p, lp);	/* Copy PathName */
		if(ismult) 	*(p++) = '/';
  	}
  	else	p = buffer;

  	for (i = __PATH_; --i>=0; )
  	{	if (comp1[i][1]) p += oscopy(p, name     + comp1[i][0], comp1[i][1]);
		else		 p += oscopy(p, template + comp2[i][0], comp2[i][1]);
  	}
  	*p = EOS;				/* Terminate with EOS	*/

	if (ismult && (next_path == ','))	/* Check File Exists	*/
	{
	 	if (access(buffer, F_OK) != 0)
	 		continue;
	}
	break;
  }

  if (ismult)
  	*(pname + lpath - 1) = ismult;

  if(isvms)	strlower(buffer);

  return(buffer);
}

/*======================================================================*/
char *osftr(name)
/*+++
.PURPOSE Convert filename.
.RETURNS The address of the converted filename.
---*/
	char     *name;		/* IN: File name			*/
{ 

  if (name != buffer)	osfsupply(name, "");

  return(buffer);
}

