/*
  vmsify.c

  Module for vms <-> unix file name conversion

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

*/

#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, char **from, char upto, int as_dir)
{
  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
	{
	  if (islower (**from))
	    **to = toupper (**from);
	  else
	    **to = **from;
	}
      (*to)++;
      (*from)++;
    }

  return 0;
}


/*
  get translation of logical name

*/

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

  INIT_DSC_CSTRING (name_dsc, name);

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

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

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

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
*/

char *
vmsify (name, type)
    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];
  char *fptr;
  char *vptr;
  char *s,*s1;
  int as_dir;
  int count;

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

  /* case 25a */

  s = strpbrk (name, "$:");
  if (s != 0)
    {
      if (*s == '$')
	{
	  if (strchr (name, '/') == 0)
	    {
	      return name;
	    }
	}
      else
	{
	  return name;
	}
    }

  /* case 26 */

  s = strchr (name, '[');

  if (s != 0)
    {
      s1 = strchr (s+1, '[');
      if (s1 == 0)
	{
	  return name;			/* single [, keep unchanged */
	}
      s1--;
      if (*s1 != ']')
	{
	  return name;			/* 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;
      int rooted = 1;	/* flag if logical is rooted, else insert [000000] */

      state = 0;

      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 */
	    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 */
	    while (*fptr == '/')	/* collapse all '/' */
	      fptr++;
	    if (*fptr == 0)		/* just // */
	      {
		char cwdbuf[MAXPATHLEN+1];

		s1 = getcwd(cwdbuf, MAXPATHLEN);
		if (s1 == 0)
		  {
		    return "";		/* FIXME, err getcwd */
		  }
		s = strchr (s1, ':');
		if (s == 0)
		  {
		    return "";		/* 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 */
	    s1 = trnlog (s);
	    if (*s1 != 0)
	      {			/* found translation */
		char *s2;
		for (;;)	/* loop over all nested logicals */
		  {
		    s2 = s1 + strlen (s1) - 1;
		    if (*s2 == ':')	/* translation ends in ':' */
		      {
			s2 = trnlog (s1);
			free (s1);
			if (*s2 == 0)
			  {
			    rooted = 0;
			    break;
			  }
			s1 = s2;
			continue;	/* next iteration */
		      }
		    if (*s2 == ']')	/* translation ends in ']' */
		      {
			if (*(s2-1) == '.')	/* ends in '.]' */
			  {
			    if (!strneq (fptr, "000000", 6))
			      rooted = 0;
			  }
			else
			  {
			    strcpy (vmsname, s1);
			    s = strchr (vmsname, ']');
			    *s = '.';
			    nstate = N_DOT;
			    vptr = s;
			  }
		      }
		    break;
		  }
		free (s1);
	      }
	    else
	      rooted = 0;

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

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

	/* s1-> '.' 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)
		 && (s1 != 0)
		 && (*(s+1) == 0))
		{
		  if (type == 2)
		    {
		      *s1 = ']';
		      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 */
	    *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))
	      {
		s = vptr-1;
		while (s > vmsname)
		  {
		    if (*s == ']')
		      {
			break;
		      }
		    if (*s == '.')
		      {
			*s = ']';
			break;
		      }
		    s--;
		  }
	      }
	    strcpy (vptr, ".dir");
	    vptr += 4;
	    state = -1;
	    break;

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

	  case 9:				/* 17..21, fptr -> 1st '/' + 1 */
	    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)) )
	      {
		fptr += 2;
		if (*fptr == '/')
		  {
		    do
		      {
			fptr++;
		      }
		    while (*fptr == '/');
		  }
		else if (*fptr == 0)
		  type = 1;
		vptr--;				/* vptr -> '.' or ']' */
		s1 = vptr;
		for (;;)
		  {
		    s1--;
		    if (*s1 == '.')		/* one back */
		      {
			vptr = s1;
			nstate = N_OPEN;
			break;
		      }
		    if (*s1 == '[')		/* top level reached */
		      {
			if (*fptr == 0)
			  {
			    strcpy (s1, "[000000]");
			    vptr = s1 + 8;
			    nstate = N_CLOSED;
			    s = 0;
			    break;
			  }
			else
			  {
			    vptr = s1+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 */
		  {
		    return name;
		  }
		do				/* got ../ */
		  {
		    fptr++;
		    while (*fptr == '/') fptr++;
		    if (*fptr != '.')
		      break;
		    if (*(fptr+1) != '.')
		      break;
		    fptr += 2;
		    if ((*fptr == 0)
			|| (*fptr == '/'))
		      count++;
		  }
		while (*fptr == '/');
	      }
	    {					/* got '..' or '../' */
	      nstate = N_OPEN;
	      *vptr++ = '[';
	      while (count--)
		*vptr++ = '-';

	      if (*fptr == 0)	/* had '..' or '../' */
		{
		  *vptr++ = ']';
		  state = -1;
		}
	      else			/* had '../xxx' */
		{
		  state = 9;
		}
	      *vptr = 0;
	    }
	    break;

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

	    if (*fptr)
	      {
		state = 9;

		switch (type)
		  {
		  case 0:
		    nstate = N_CLOSED;
		    *vptr++ = '[';
		    *vptr++ = ']';
		    break;

		  case 1:
		    nstate = N_OPEN;
		    *vptr++ = '[';
		    break;

		  case 2:
		    nstate = N_CLOSED;
		    break;
		  }
	      }
	    else
	      {
		if (type == 1)
		  {
		    *vptr++ = '[';
		    *vptr++ = ']';
		    state = -1;
		  }
		else
		  {
		    char cwdbuf[MAXPATHLEN+1];

		    s1 = getcwd(cwdbuf, MAXPATHLEN);
		    if (s1 == 0)
		      {
			return "foo";	    /*FIXME, err getcwd */
		      }
		    strcpy (vptr, s1);
		    vptr += strlen (vptr);

		    if (type == 2)
		      {
			s = vptr;
			while (s > vmsname)
			  {
			    if (*s == '.')
			      {
				*s = ']';
				vptr--;
				break;
			      }

			    if (*s == '[')
			      {
				int i;
				char *t = vptr - 2;
				while (t > s)
				  {
				    *(t+7) = *t;
				    t--;
				  }
				s++;
				for (i = 0; i < 6; i++)
				  *s++ = '0';
				*s = ']';
				vptr += 6;
				break;
			      }
			    s--;
			  }

			strcpy (vptr, ".dir");
			vptr += 4;
		      }

		    state = -1;
		  }
	      }
	    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/
*/

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

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

  p = piece;
  *p = 0;

  /* device part */

  s = strchr (name, ':');

  if (s != 0)
    {
      *s = 0;
      *p++ = '/';
      *p++ = '/';
      strcpy (p, name);
      p += strlen (p);
      *s = ':';
    }

  /* 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 */
