/* vmsify.c -- Module for vms <-> unix file name conversion
Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
2007, 2008, 2009, 2010 Free Software Foundation, Inc.
This file is part of GNU Make.

GNU Make 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 3 of the License, or (at your option) any later
version.

GNU Make 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, see <http://www.gnu.org/licenses/>.  */

/* Written by Klaus Kämpf (kkaempf@progis.de)
   of proGIS Software, Aachen, Germany */


#include <stdio.h>
#include <string.h>
#include <ctype.h>

#if VMS
#include <unixlib.h>
#include <stdlib.h>
#include <jpidef.h>
#include <descrip.h>
#include <uaidef.h>
#include <ssdef.h>
#include <starlet.h>
#include <lib$routines.h>
/* Initialize a string descriptor (struct dsc$descriptor_s) for an
   arbitrary string.   ADDR is a pointer to the first character
   of the string, and LEN is the length of the string. */

#define INIT_DSC_S(dsc, addr, len) do { \
  (dsc).dsc$b_dtype = DSC$K_DTYPE_T;    \
  (dsc).dsc$b_class = DSC$K_CLASS_S;    \
  (dsc).dsc$w_length = (len);           \
  (dsc).dsc$a_pointer = (addr);         \
} while (0)

/* Initialize a string descriptor (struct dsc$descriptor_s) for a
   NUL-terminated string.  S is a pointer to the string; the length
   is determined by calling strlen(). */

#define INIT_DSC_CSTRING(dsc, s) INIT_DSC_S(dsc, s, strlen(s))
#endif

/*
  copy 'from' to 'to' up to but not including 'upto'
  return 0 if eos on from
  return 1 if upto found

  return 'to' at last char + 1
  return 'from' at match + 1 or eos if no match

  if as_dir == 1, change all '.' to '_'
  else change all '.' but the last to '_'
*/

static int
copyto (char **to, const char **from, char upto, int as_dir)
{
  const char *s;

  s = strrchr (*from, '.');

  while (**from)
    {
      if (**from == upto)
	{
	  do
	    {
	      (*from)++;
	    }
	  while (**from == upto);
	  return 1;
	}
      if (**from == '.')
	{
	  if ((as_dir == 1)
	      || (*from != s))
	    **to = '_';
	  else
	    **to = '.';
	}
      else
	{
#ifdef HAVE_CASE_INSENSITIVE_FS
	  if (isupper ((unsigned char)**from))
	    **to = tolower ((unsigned char)**from);
	  else
#endif
	    **to = **from;
	}
      (*to)++;
      (*from)++;
    }

  return 0;
}


/*
  get translation of logical name

*/

static char *
trnlog (const char *name)
{
  int stat;
  static char reslt[1024];
  $DESCRIPTOR (reslt_dsc, reslt);
  short resltlen;
  struct dsc$descriptor_s name_dsc;
  char *s;

  /*
   * the string isn't changed, but there is no string descriptor with
   * "const char *dsc$a_pointer"
   */
  INIT_DSC_CSTRING (name_dsc, (char *)name);

  stat = lib$sys_trnlog (&name_dsc, &resltlen, &reslt_dsc);

  if ((stat&1) == 0)
    {
      return "";
    }
  if (stat == SS$_NOTRAN)
    {
      return "";
    }
  reslt[resltlen] = '\0';

  s = malloc (resltlen+1);
  if (s == 0)
    return "";
  strcpy (s, reslt);
  return s;
}

static char *
showall (char *s)
{
  static char t[512];
  char *pt;

  pt = t;
  if (strchr (s, '\\') == 0)
    return s;
  while (*s)
    {
      if (*s == '\\')
	{
	  *pt++ = *s;
	}
      *pt++ = *s++;
    }
  return pt;
}


enum namestate { N_START, N_DEVICE, N_OPEN, N_DOT, N_CLOSED, N_DONE };

/*
  convert unix style name to vms style
  type = 0 -> name is a full name (directory and filename part)
  type = 1 -> name is a directory
  type = 2 -> name is a filename without directory

  The following conversions are applied
			(0)		(1)			(2)
	input		full name	dir name		file name

1	./		<cwd>		[]			<current directory>.dir
2	../		<home of cwd>	<home of cwd>		<home of cwd>.dir

3	//		<dev of cwd>:	<dev of cwd>:[000000]	<dev of cwd>:000000.dir
4	//a		a:		a:			a:
5	//a/		a:		a:			a:000000.dir

9	/		[000000]	[000000]		000000.dir
10	/a		[000000]a	[a]			[000000]a
11	/a/		[a]		[a]			[000000]a.dir
12	/a/b		[a]b		[a.b]			[a]b
13	/a/b/		[a.b]		[a.b]			[a]b.dir
14	/a/b/c		[a.b]c		[a.b.c]			[a.b]c
15	/a/b/c/		[a.b.c]		[a.b.c]			[a.b]c.dir

16	a		a		[.a]			a
17	a/		[.a]		[.a]			a.dir
18	a/b		[.a]b		[.a.b]			[.a]b
19	a/b/		[.a.b]		[.a.b]			[.a]b.dir
20	a/b/c		[.a.b]c		[.a.b.c]		[.a.b]c
21	a/b/c/		[.a.b.c]	[.a.b.c]		[.a.b]c.dir

22	a.b.c		a_b.c		[.a_b_c]		a_b_c.dir

23	[x][y]z		[x.y]z		[x.y]z			[x.y]z
24	[x][.y]z	[x.y]z		[x.y]z			[x.y]z

25  filenames with '$'  are left unchanged if they contain no '/'
25  filenames with ':' are left unchanged
26  filenames with a single pair of '[' ']' are left unchanged

  The input string is not written to.  The result is also const because
  it's a static buffer; we don't want to change it.
*/

const char *
vmsify (const char *name, int type)
{
/* max 255 device
   max 39 directory
   max 39 filename
   max 39 filetype
   max 5 version
*/
#define MAXPATHLEN 512

  enum namestate nstate;
  static char vmsname[MAXPATHLEN+1];
  const char *fptr;
  const char *t;
  char *vptr;
  int as_dir;
  int count;
  const char *s;
  const char *s1;
  const char *s2;

  if (name == 0)
    return 0;
  fptr = name;
  vptr = vmsname;
  nstate = N_START;

  /* case 25a */
  t = strpbrk (name, "$:");

  if (t != 0)
    {
//      const char *s1;
//      const char *s2;

      if (type == 1)
        {
          s1 = strchr (t+1, '[');
          s2 = strchr (t+1, ']');
        }

      if (*t == '$')
        {
          if (strchr (name, '/') == 0)
            {
              strcpy (vmsname, name);
              if ((type == 1) && (s1 != 0) && (s2 == 0))
                strcat (vmsname, "]");
              return vmsname;
            }
        }
      else
        {
          strcpy (vmsname, name);
          if ((type == 1) && (s1 != 0) && (s2 == 0))
            strcat (vmsname, "]");
          return vmsname;
        }
    }

  /* case 26 */
  t = strchr (name, '[');

  if (t != 0)
    {
//      const char *s;
//      const char *s1 = strchr (t+1, '[');
      s1 = strchr (t+1, '[');
      if (s1 == 0)
	{
          strcpy (vmsname, name);
	  if ((type == 1) && (strchr (t+1, ']') == 0))
            strcat (vmsname, "]");
          return vmsname;
	}
      s1--;
      if (*s1 != ']')
	{
          strcpy (vmsname, name);
	  return vmsname;		/* not ][, keep unchanged */
	}

      /* we have ][ */

      s = name;

      /* s  -> starting char
	 s1 -> ending ']'  */
      do
	{
	  strncpy (vptr, s, s1-s);	/* copy up to but not including ']' */
	  vptr += s1-s;
	  if (*s1 == 0)
	    break;
	  s = s1 + 1;			/* s -> char behind ']' */
	  if (*s != '[')		/* was '][' ? */
	    break;			/* no, last ] found, exit */
	  s++;
	  if (*s != '.')
	    *vptr++ = '.';
	  s1 = strchr (s, ']');
	  if (s1 == 0)			/* no closing ] */
	    s1 = s + strlen (s);
	}
      while (1);

      *vptr++ = ']';

      fptr = s;

    }
  else		/* no [ in name */
    {
      int state = 0;
      int rooted = 1;	/* flag if logical is rooted, else insert [000000] */

      do
	{
      switch (state)
	{
	  case 0:				/* start of loop */
	    if (*fptr == '/')
	      {
		fptr++;
		state = 1;
	      }
	    else if (*fptr == '.')
	      {
		fptr++;
		state = 10;
	      }
	    else
	      state = 2;
	    break;

	  case 1:				/* '/' at start */
	    if (*fptr == '/')
	      {
		fptr++;
		state = 3;
	      }
	    else
	      state = 4;
	    break;

	  case 2:				/* no '/' at start */
            {
            const char *s = strchr (fptr, '/');
	    if (s == 0)			/* no '/' (16) */
	      {
		if (type == 1)
		  {
		    strcpy (vptr, "[.");
		    vptr += 2;
		  }
		copyto (&vptr, &fptr, 0, (type==1));
		if (type == 1)
		  *vptr++ = ']';
		state = -1;
	      }
	    else			/* found '/' (17..21) */
	      {
		if ((type == 2)
		    && (*(s+1) == 0))	/* 17(2) */
		  {
		    copyto (&vptr, &fptr, '/', 1);
		    state = 7;
		  }
		else
		  {
		    strcpy (vptr, "[.");
		    vptr += 2;
		    copyto (&vptr, &fptr, '/', 1);
		    nstate = N_OPEN;
		    state = 9;
		  }
	      }
	    break;
            }

	  case 3:				/* '//' at start */
            {
//            const char *s;
//            const char *s1;
            char *vp;
	    while (*fptr == '/')	/* collapse all '/' */
	      fptr++;
	    if (*fptr == 0)		/* just // */
	      {
		char cwdbuf[MAXPATHLEN+1];

		s1 = getcwd(cwdbuf, MAXPATHLEN);
		if (s1 == 0)
		  {
                    vmsname[0] = '\0';
		    return vmsname;	/* FIXME, err getcwd */
		  }
		s = strchr (s1, ':');
		if (s == 0)
		  {
                    vmsname[0] = '\0';
		    return vmsname;	/* FIXME, err no device */
		  }
		strncpy (vptr, s1, s-s1+1);
		vptr += s-s1+1;
		state = -1;
		break;
	      }

	    s = vptr;

	    if (copyto (&vptr, &fptr, '/', 1) == 0)	/* copy device part */
	      {
		*vptr++ = ':';
		state = -1;
		break;
	      }
	    *vptr = ':';
	    nstate = N_DEVICE;
	    if (*fptr == 0)	/* just '//a/' */
	      {
		strcpy (vptr+1, "[000000]");
		vptr += 9;
		state = -1;
		break;
	      }
	    *vptr = 0;
				/* check logical for [000000] insertion */
	    vp = trnlog (s);
	    if (*vp != '\0')
	      {			/* found translation */
		for (;;)	/* loop over all nested logicals */
		  {
		    char *vp2 = vp + strlen (vp) - 1;
		    if (*vp2 == ':')	/* translation ends in ':' */
		      {
			vp2 = trnlog (vp);
			free (vp);
			if (*vp2 == 0)
			  {
			    rooted = 0;
			    break;
			  }
			vp = vp2;
			continue;	/* next iteration */
		      }
		    if (*vp2 == ']')	/* translation ends in ']' */
		      {
			if (*(vp2-1) == '.')	/* ends in '.]' */
			  {
			    if (strncmp (fptr, "000000", 6) != 0)
			      rooted = 0;
			  }
			else
			  {
			    strcpy (vmsname, s1);
			    vp = strchr (vmsname, ']');
			    *vp = '.';
			    nstate = N_DOT;
			    vptr = vp;
			  }
		      }
		    break;
		  }
		free (vp);
	      }
	    else
	      rooted = 0;

	    if (*vptr == 0)
	      {
		nstate = N_DEVICE;
	        *vptr++ = ':';
	      }
	    else
	      vptr++;

	    if (rooted == 0)
	      {
		nstate = N_DOT;
	        strcpy (vptr, "[000000.");
		vptr += 8;
		vp = vptr-1;
	      }
	    else
	      vp = 0;

            /* vp-> '.' after 000000 or NULL */

	    s = strchr (fptr, '/');
	    if (s == 0)
	      {				/* no next '/' */
		if (*(vptr-1) == '.')
		  *(vptr-1) = ']';
		else if (rooted == 0)
		  *vptr++ = ']';
		copyto (&vptr, &fptr, 0, (type == 1));
		state = -1;
		break;
	      }
	    else
	      {
		while (*(s+1) == '/')	/* skip multiple '/' */
		  s++;
	      }

	    if ((rooted != 0)
	        && (*(vptr-1) != '.'))
	      {
		*vptr++ = '[';
		nstate = N_DOT;
	      }
	    else
	      if ((nstate == N_DOT)
		 && (vp != 0)
		 && (*(s+1) == 0))
		{
		  if (type == 2)
		    {
		      *vp = ']';
		      nstate = N_CLOSED;
		    }
		}
	    state = 9;
	    break;
            }
	  case 4:				/* single '/' at start (9..15) */
	    if (*fptr == 0)
	      state = 5;
	    else
	      state = 6;
	    break;

	  case 5:				/* just '/' at start (9) */
	    if (type != 2)
	      {
	        *vptr++ = '[';
		nstate = N_OPEN;
	      }
	    strcpy (vptr, "000000");
	    vptr += 6;
	    if (type == 2)
	      state = 7;
	    else
	      state = 8;
	    break;

	  case 6:		/* chars following '/' at start 10..15 */
            {
            const char *s;
	    *vptr++ = '[';
	    nstate = N_OPEN;
	    s = strchr (fptr, '/');
	    if (s == 0)			/* 10 */
	      {
		if (type != 1)
		  {
		    strcpy (vptr, "000000]");
		    vptr += 7;
		  }
		copyto (&vptr, &fptr, 0, (type == 1));
		if (type == 1)
		  {
		    *vptr++ = ']';
		  }
		state = -1;
	      }
	    else			/* 11..15 */
	      {
		if ( (type == 2)
		   && (*(s+1) == 0))	/* 11(2) */
		  {
		    strcpy (vptr, "000000]");
		    nstate = N_CLOSED;
		    vptr += 7;
		  }
		copyto (&vptr, &fptr, '/', (*(vptr-1) != ']'));
		state = 9;
	      }
	    break;
            }

	  case 7:				/* add '.dir' and exit */
	    if ((nstate == N_OPEN)
		|| (nstate == N_DOT))
	      {
		char *vp = vptr-1;
		while (vp > vmsname)
		  {
		    if (*vp == ']')
		      {
			break;
		      }
		    if (*vp == '.')
		      {
			*vp = ']';
			break;
		      }
		    vp--;
		  }
	      }
	    strcpy (vptr, ".dir");
	    vptr += 4;
	    state = -1;
	    break;

	  case 8:				/* add ']' and exit */
	    *vptr++ = ']';
	    state = -1;
	    break;

	  case 9:			/* 17..21, fptr -> 1st '/' + 1 */
            {
            const char *s;
	    if (*fptr == 0)
	      {
		if (type == 2)
		  {
		    state = 7;
		  }
		else
		  state = 8;
		break;
	      }
	    s = strchr (fptr, '/');
	    if (s == 0)
	      {
		if (type != 1)
		  {
		    if (nstate == N_OPEN)
		      {
			*vptr++ = ']';
			nstate = N_CLOSED;
		      }
		    as_dir = 0;
		  }
		else
		  {
		    if (nstate == N_OPEN)
		      {
			*vptr++ = '.';
			nstate = N_DOT;
		      }
		    as_dir = 1;
		  }
	      }
	    else
	      {
		while (*(s+1) == '/')
		  s++;
		if ( (type == 2)
		    && (*(s+1) == 0))		/* 19(2), 21(2)*/
		  {
		    if (nstate != N_CLOSED)
		      {
			*vptr++ = ']';
			nstate = N_CLOSED;
		      }
		    as_dir = 1;
		  }
		else
		  {
		    if (nstate == N_OPEN)
		      {
			*vptr++ = '.';
			nstate = N_DOT;
		      }
		    as_dir = 1;
		  }
	      }
	    if ( (*fptr == '.')			/* check for '..' or '../' */
		&& (*(fptr+1) == '.')
		&& ((*(fptr+2) == '/')
		    || (*(fptr+2) == 0)) )
	      {
                char *vp;
		fptr += 2;
		if (*fptr == '/')
		  {
		    do
		      {
			fptr++;
		      }
		    while (*fptr == '/');
		  }
		else if (*fptr == 0)
		  type = 1;
		vptr--;				/* vptr -> '.' or ']' */
		vp = vptr;
		for (;;)
		  {
		    vp--;
		    if (*vp == '.')		/* one back */
		      {
			vptr = vp;
			nstate = N_OPEN;
			break;
		      }
		    if (*vp == '[')		/* top level reached */
		      {
			if (*fptr == 0)
			  {
			    strcpy (vp, "[000000]");
			    vptr = vp + 8;
			    nstate = N_CLOSED;
			    s = 0;
			    break;
			  }
			else
			  {
			    vptr = vp+1;
			    nstate = N_OPEN;
			    break;
			  }
		      }
		  }
	      }
	    else
	      {
		copyto (&vptr, &fptr, '/', as_dir);
		if (nstate == N_DOT)
		  nstate = N_OPEN;
	      }
	    if (s == 0)
	      {					/* 18,20 */
		if (type == 1)
		  *vptr++ = ']';
		state = -1;
	      }
	    else
	      {
		if (*(s+1) == 0)
		  {
		    if (type == 2)		/* 19,21 */
		      {
		        state = 7;
		      }
		    else
		      {
			*vptr++ = ']';
			state = -1;
		      }
		  }
	      }
	    break;
            }

	  case 10:				/* 1,2 first is '.' */
	    if (*fptr == '.')
	      {
		fptr++;
		state = 11;
	      }
	    else
	      state = 12;
	    break;

	  case 11:				/* 2, '..' at start */
	    count = 1;
	    if (*fptr != 0)
	      {
		if (*fptr != '/')		/* got ..xxx */
		  {
                    strcpy (vmsname, name);
		    return vmsname;
		  }
		do				/* got ../ */
		  {
		    fptr++;
		    while (*fptr == '/') fptr++;
		    if (*fptr != '.')
		      break;
		    if (*(fptr+1) != '.')
		      break;
		    fptr += 2;
		    if ((*fptr == 0)
			|| (*fptr == '/'))
		      count++;
		  }
		while (*fptr == '/');
	      }
	    {					/* got '..' or '../' */
              char *vp;
	      char cwdbuf[MAXPATHLEN+1];

	      vp = getcwd(cwdbuf, MAXPATHLEN);
	      if (vp == 0)
		{
                  vmsname[0] = '\0';
		  return vmsname;    /* FIXME, err getcwd */
		}
	      strcpy (vptr, vp);
	      vp = strchr (vptr, ']');
	      if (vp != 0)
		{
		  nstate = N_OPEN;
		  while (vp > vptr)
		    {
		      vp--;
		      if (*vp == '[')
			{
			  vp++;
			  strcpy (vp, "000000]");
			  state = -1;
			  break;
			}
		      else if (*vp == '.')
			{
			  if (--count == 0)
			    {
			      if (*fptr == 0)	/* had '..' or '../' */
				{
				  *vp++ = ']';
				  state = -1;
				}
			      else			/* had '../xxx' */
				{
				  state = 9;
				}
			      *vp = '\0';
			      break;
			    }
			}
		    }
		}
	      vptr += strlen (vptr);
	    }
	    break;

	  case 12:				/* 1, '.' at start */
	    if (*fptr != 0)
	      {
		if (*fptr != '/')
		  {
                    strcpy (vmsname, name);
		    return vmsname;
		  }
		while (*fptr == '/')
		  fptr++;
	      }

	    {
              char *vp;
	      char cwdbuf[MAXPATHLEN+1];

	      vp = getcwd(cwdbuf, MAXPATHLEN);
	      if (vp == 0)
		{
                  vmsname[0] = '\0';
		  return vmsname;    /*FIXME, err getcwd */
		}
	      strcpy (vptr, vp);
            }
            if (*fptr == 0)
              {
                state = -1;
                break;
              }
            else
              {
                char *vp = strchr (vptr, ']');
                if (vp == 0)
                  {
                    state = -1;
                    break;
                  }
                *vp = '\0';
                nstate = N_OPEN;
                vptr += strlen (vptr);
                state = 9;
              }
	    break;
	}

	}
      while (state > 0);


    }


  /* directory conversion done
     fptr -> filename part of input string
     vptr -> free space in vmsname
  */

  *vptr++ = 0;

  return vmsname;
}



/*
  convert from vms-style to unix-style

  dev:[dir1.dir2]	//dev/dir1/dir2/
*/

const char *
unixify (const char *name)
{
  static char piece[512];
  const char *s;
  char *p;

  if (strchr (name, '/') != 0)		/* already in unix style */
    {
      strcpy (piece, name);
      return piece;
    }

  p = piece;
  *p = 0;

  /* device part */

  s = strchr (name, ':');

  if (s != 0)
    {
      int l = s - name;
      *p++ = '/';
      *p++ = '/';
      strncpy (p, name, l);
      p += l;
    }

  /* directory part */

  *p++ = '/';
  s = strchr (name, '[');

  if (s != 0)
    {
      s++;
      switch (*s)
        {
	  case ']':		/* [] */
	    strcat (p, "./");
	    break;
	  case '-':		/* [- */
	    strcat (p, "../");
	    break;
	  case '.':
	    strcat (p, "./");	/* [. */
	    break;
	  default:
	    s--;
	    break;
        }
      s++;
      while (*s)
        {
	  if (*s == '.')
	    *p++ = '/';
	  else
	    *p++ = *s;
	  s++;
	  if (*s == ']')
	    {
	      s++;
	      break;
	    }
        }
      if (*s != 0)		/* more after ']' ?? */
        {
	  if (*(p-1) != '/')
	    *p++ = '/';
	  strcpy (p, s);		/* copy it anyway */
        }
    }

  else		/* no '[' anywhere */

    {
      *p++ = 0;
    }

  /* force end with '/' */

  if (*(p-1) != '/')
    *p++ = '/';
  *p = 0;

  return piece;
}

/* EOF */
