1/*
2  vmsify.c
3
4  Module for vms <-> unix file name conversion
5
6  Written by Klaus K�mpf (kkaempf@progis.de)
7  of proGIS Software, Aachen, Germany
8
9*/
10
11#include <stdio.h>
12#include <string.h>
13#include <ctype.h>
14
15#if VMS
16#include <unixlib.h>
17#include <stdlib.h>
18#include <jpidef.h>
19#include <descrip.h>
20#include <uaidef.h>
21#include <ssdef.h>
22#include <starlet.h>
23#include <lib$routines.h>
24/* Initialize a string descriptor (struct dsc$descriptor_s) for an
25   arbitrary string.   ADDR is a pointer to the first character
26   of the string, and LEN is the length of the string. */
27
28#define INIT_DSC_S(dsc, addr, len) do { \
29  (dsc).dsc$b_dtype = DSC$K_DTYPE_T;    \
30  (dsc).dsc$b_class = DSC$K_CLASS_S;    \
31  (dsc).dsc$w_length = (len);           \
32  (dsc).dsc$a_pointer = (addr);         \
33} while (0)
34
35/* Initialize a string descriptor (struct dsc$descriptor_s) for a
36   NUL-terminated string.  S is a pointer to the string; the length
37   is determined by calling strlen(). */
38
39#define INIT_DSC_CSTRING(dsc, s) INIT_DSC_S(dsc, s, strlen(s))
40#endif
41
42/*
43  copy 'from' to 'to' up to but not including 'upto'
44  return 0 if eos on from
45  return 1 if upto found
46
47  return 'to' at last char + 1
48  return 'from' at match + 1 or eos if no match
49
50  if as_dir == 1, change all '.' to '_'
51  else change all '.' but the last to '_'
52*/
53
54static int
55copyto (char **to, char **from, char upto, int as_dir)
56{
57  char *s;
58
59  s = strrchr (*from, '.');
60
61  while (**from)
62    {
63      if (**from == upto)
64	{
65	  do
66	    {
67	      (*from)++;
68	    }
69	  while (**from == upto);
70	  return 1;
71	}
72      if (**from == '.')
73	{
74	  if ((as_dir == 1)
75	      || (*from != s))
76	    **to = '_';
77	  else
78	    **to = '.';
79	}
80      else
81	{
82	  if (isupper ((unsigned char)**from))
83	    **to = tolower ((unsigned char)**from);
84	  else
85	    **to = **from;
86	}
87      (*to)++;
88      (*from)++;
89    }
90
91  return 0;
92}
93
94
95/*
96  get translation of logical name
97
98*/
99
100static char *
101trnlog (char *name)
102{
103  int stat;
104  static char reslt[1024];
105  $DESCRIPTOR (reslt_dsc, reslt);
106  short resltlen;
107  struct dsc$descriptor_s name_dsc;
108  char *s;
109
110  INIT_DSC_CSTRING (name_dsc, name);
111
112  stat = lib$sys_trnlog (&name_dsc, &resltlen, &reslt_dsc);
113
114  if ((stat&1) == 0)
115    {
116      return "";
117    }
118  if (stat == SS$_NOTRAN)
119    {
120      return "";
121    }
122  reslt[resltlen] = '\0';
123
124  s = (char *)malloc (resltlen+1);
125  if (s == 0)
126    return "";
127  strcpy (s, reslt);
128  return s;
129}
130
131static char *
132showall (char *s)
133{
134  static char t[512];
135  char *pt;
136
137  pt = t;
138  if (strchr (s, '\\') == 0)
139    return s;
140  while (*s)
141    {
142      if (*s == '\\')
143	{
144	  *pt++ = *s;
145	}
146      *pt++ = *s++;
147    }
148  return pt;
149}
150
151
152enum namestate { N_START, N_DEVICE, N_OPEN, N_DOT, N_CLOSED, N_DONE };
153
154/*
155  convert unix style name to vms style
156  type = 0 -> name is a full name (directory and filename part)
157  type = 1 -> name is a directory
158  type = 2 -> name is a filename without directory
159
160  The following conversions are applied
161			(0)		(1)			(2)
162	input		full name	dir name		file name
163
1641	./		<cwd>		[]			<current directory>.dir
1652	../		<home of cwd>	<home of cwd>		<home of cwd>.dir
166
1673	//		<dev of cwd>:	<dev of cwd>:[000000]	<dev of cwd>:000000.dir
1684	//a		a:		a:			a:
1695	//a/		a:		a:			a:000000.dir
170
1719	/		[000000]	[000000]		000000.dir
17210	/a		[000000]a	[a]			[000000]a
17311	/a/		[a]		[a]			[000000]a.dir
17412	/a/b		[a]b		[a.b]			[a]b
17513	/a/b/		[a.b]		[a.b]			[a]b.dir
17614	/a/b/c		[a.b]c		[a.b.c]			[a.b]c
17715	/a/b/c/		[a.b.c]		[a.b.c]			[a.b]c.dir
178
17916	a		a		[.a]			a
18017	a/		[.a]		[.a]			a.dir
18118	a/b		[.a]b		[.a.b]			[.a]b
18219	a/b/		[.a.b]		[.a.b]			[.a]b.dir
18320	a/b/c		[.a.b]c		[.a.b.c]		[.a.b]c
18421	a/b/c/		[.a.b.c]	[.a.b.c]		[.a.b]c.dir
185
18622	a.b.c		a_b.c		[.a_b_c]		a_b_c.dir
187
18823	[x][y]z		[x.y]z		[x.y]z			[x.y]z
18924	[x][.y]z	[x.y]z		[x.y]z			[x.y]z
190
19125  filenames with '$'  are left unchanged if they contain no '/'
19225  filenames with ':' are left unchanged
19326  filenames with a single pair of '[' ']' are left unchanged
194
195  the input string is not written to
196*/
197
198char *
199vmsify (name, type)
200    char *name;
201    int type;
202{
203/* max 255 device
204   max 39 directory
205   max 39 filename
206   max 39 filetype
207   max 5 version
208*/
209#define MAXPATHLEN 512
210
211  enum namestate nstate;
212  static char vmsname[MAXPATHLEN+1];
213  char *fptr;
214  char *vptr;
215  char *s,*s1;
216  int as_dir;
217  int count;
218
219  if (name == 0)
220    return 0;
221  fptr = name;
222  vptr = vmsname;
223  nstate = N_START;
224
225  /* case 25a */
226
227  s = strpbrk (name, "$:");
228  if (s != 0)
229    {
230      char *s1;
231      char *s2;
232
233      if (type == 1)
234	{
235	  s1 = strchr (s+1, '[');
236	  s2 = strchr (s+1, ']');
237	}
238
239      if (*s == '$')
240	{
241	  if (strchr (name, '/') == 0)
242	    {
243	      if ((type == 1) && (s1 != 0) && (s2 == 0))
244		{
245		  strcpy (vmsname, name);
246		  strcat (vmsname, "]");
247		  return vmsname;
248		}
249	      else
250		return name;
251	    }
252	}
253      else
254	{
255	  if ((type == 1) && (s1 != 0) && (s2 == 0))
256	    {
257	      strcpy (vmsname, name);
258	      strcat (vmsname, "]");
259	      return vmsname;
260	    }
261	  else
262	    return name;
263	}
264    }
265
266  /* case 26 */
267
268  s = strchr (name, '[');
269
270  if (s != 0)
271    {
272      s1 = strchr (s+1, '[');
273      if (s1 == 0)
274	{
275	  if ((type == 1)
276	       && (strchr (s+1, ']') == 0))
277	    {
278	      strcpy (vmsname, name);
279	      strcat (vmsname, "]");
280	      return vmsname;
281	    }
282	  else
283	    return name;			/* single [, keep unchanged */
284	}
285      s1--;
286      if (*s1 != ']')
287	{
288	  return name;			/* not ][, keep unchanged */
289	}
290
291      /* we have ][ */
292
293      s = name;
294
295      /* s  -> starting char
296	 s1 -> ending ']'  */
297
298      do
299	{
300	  strncpy (vptr, s, s1-s);	/* copy up to but not including ']' */
301	  vptr += s1-s;
302	  if (*s1 == 0)
303	    break;
304	  s = s1 + 1;			/* s -> char behind ']' */
305	  if (*s != '[')		/* was '][' ? */
306	    break;			/* no, last ] found, exit */
307	  s++;
308	  if (*s != '.')
309	    *vptr++ = '.';
310	  s1 = strchr (s, ']');
311	  if (s1 == 0)			/* no closing ] */
312	    s1 = s + strlen (s);
313	}
314      while (1);
315
316      *vptr++ = ']';
317
318      fptr = s;
319
320    }
321
322  else		/* no [ in name */
323
324    {
325
326      int state;
327      int rooted = 1;	/* flag if logical is rooted, else insert [000000] */
328
329      state = 0;
330
331      do
332	{
333
334      switch (state)
335	{
336	  case 0:				/* start of loop */
337	    if (*fptr == '/')
338	      {
339		fptr++;
340		state = 1;
341	      }
342	    else if (*fptr == '.')
343	      {
344		fptr++;
345		state = 10;
346	      }
347	    else
348	      state = 2;
349	    break;
350
351	  case 1:				/* '/' at start */
352	    if (*fptr == '/')
353	      {
354		fptr++;
355		state = 3;
356	      }
357	    else
358	      state = 4;
359	    break;
360
361	  case 2:				/* no '/' at start */
362	    s = strchr (fptr, '/');
363	    if (s == 0)			/* no '/' (16) */
364	      {
365		if (type == 1)
366		  {
367		    strcpy (vptr, "[.");
368		    vptr += 2;
369		  }
370		copyto (&vptr, &fptr, 0, (type==1));
371		if (type == 1)
372		  *vptr++ = ']';
373		state = -1;
374	      }
375	    else			/* found '/' (17..21) */
376	      {
377		if ((type == 2)
378		    && (*(s+1) == 0))	/* 17(2) */
379		  {
380		    copyto (&vptr, &fptr, '/', 1);
381		    state = 7;
382		  }
383		else
384		  {
385		    strcpy (vptr, "[.");
386		    vptr += 2;
387		    copyto (&vptr, &fptr, '/', 1);
388		    nstate = N_OPEN;
389		    state = 9;
390		  }
391	      }
392	    break;
393
394	  case 3:				/* '//' at start */
395	    while (*fptr == '/')	/* collapse all '/' */
396	      fptr++;
397	    if (*fptr == 0)		/* just // */
398	      {
399		char cwdbuf[MAXPATHLEN+1];
400
401		s1 = getcwd(cwdbuf, MAXPATHLEN);
402		if (s1 == 0)
403		  {
404		    return "";		/* FIXME, err getcwd */
405		  }
406		s = strchr (s1, ':');
407		if (s == 0)
408		  {
409		    return "";		/* FIXME, err no device */
410		  }
411		strncpy (vptr, s1, s-s1+1);
412		vptr += s-s1+1;
413		state = -1;
414		break;
415	      }
416
417	    s = vptr;
418
419	    if (copyto (&vptr, &fptr, '/', 1) == 0)	/* copy device part */
420	      {
421		*vptr++ = ':';
422		state = -1;
423		break;
424	      }
425	    *vptr = ':';
426	    nstate = N_DEVICE;
427	    if (*fptr == 0)	/* just '//a/' */
428	      {
429		strcpy (vptr+1, "[000000]");
430		vptr += 9;
431		state = -1;
432		break;
433	      }
434	    *vptr = 0;
435				/* check logical for [000000] insertion */
436	    s1 = trnlog (s);
437	    if (*s1 != 0)
438	      {			/* found translation */
439		char *s2;
440		for (;;)	/* loop over all nested logicals */
441		  {
442		    s2 = s1 + strlen (s1) - 1;
443		    if (*s2 == ':')	/* translation ends in ':' */
444		      {
445			s2 = trnlog (s1);
446			free (s1);
447			if (*s2 == 0)
448			  {
449			    rooted = 0;
450			    break;
451			  }
452			s1 = s2;
453			continue;	/* next iteration */
454		      }
455		    if (*s2 == ']')	/* translation ends in ']' */
456		      {
457			if (*(s2-1) == '.')	/* ends in '.]' */
458			  {
459			    if (strncmp (fptr, "000000", 6) != 0)
460			      rooted = 0;
461			  }
462			else
463			  {
464			    strcpy (vmsname, s1);
465			    s = strchr (vmsname, ']');
466			    *s = '.';
467			    nstate = N_DOT;
468			    vptr = s;
469			  }
470		      }
471		    break;
472		  }
473		free (s1);
474	      }
475	    else
476	      rooted = 0;
477
478	    if (*vptr == 0)
479	      {
480		nstate = N_DEVICE;
481	        *vptr++ = ':';
482	      }
483	    else
484	      vptr++;
485
486	    if (rooted == 0)
487	      {
488	        strcpy (vptr, "[000000.");
489		vptr += 8;
490		s1 = vptr-1;
491		nstate = N_DOT;
492	      }
493	    else
494	      s1 = 0;
495
496	/* s1-> '.' after 000000 or NULL */
497
498	    s = strchr (fptr, '/');
499	    if (s == 0)
500	      {				/* no next '/' */
501		if (*(vptr-1) == '.')
502		  *(vptr-1) = ']';
503		else if (rooted == 0)
504		  *vptr++ = ']';
505		copyto (&vptr, &fptr, 0, (type == 1));
506		state = -1;
507		break;
508	      }
509	    else
510	      {
511		while (*(s+1) == '/')	/* skip multiple '/' */
512		  s++;
513	      }
514
515	    if ((rooted != 0)
516	        && (*(vptr-1) != '.'))
517	      {
518		*vptr++ = '[';
519		nstate = N_DOT;
520	      }
521	    else
522	      if ((nstate == N_DOT)
523		 && (s1 != 0)
524		 && (*(s+1) == 0))
525		{
526		  if (type == 2)
527		    {
528		      *s1 = ']';
529		      nstate = N_CLOSED;
530		    }
531		}
532	    state = 9;
533	    break;
534
535	  case 4:				/* single '/' at start (9..15) */
536	    if (*fptr == 0)
537	      state = 5;
538	    else
539	      state = 6;
540	    break;
541
542	  case 5:				/* just '/' at start (9) */
543	    if (type != 2)
544	      {
545	        *vptr++ = '[';
546		nstate = N_OPEN;
547	      }
548	    strcpy (vptr, "000000");
549	    vptr += 6;
550	    if (type == 2)
551	      state = 7;
552	    else
553	      state = 8;
554	    break;
555
556	  case 6:				/* chars following '/' at start 10..15 */
557	    *vptr++ = '[';
558	    nstate = N_OPEN;
559	    s = strchr (fptr, '/');
560	    if (s == 0)			/* 10 */
561	      {
562		if (type != 1)
563		  {
564		    strcpy (vptr, "000000]");
565		    vptr += 7;
566		  }
567		copyto (&vptr, &fptr, 0, (type == 1));
568		if (type == 1)
569		  {
570		    *vptr++ = ']';
571		  }
572		state = -1;
573	      }
574	    else			/* 11..15 */
575	      {
576		if ( (type == 2)
577		   && (*(s+1) == 0))	/* 11(2) */
578		  {
579		    strcpy (vptr, "000000]");
580		    nstate = N_CLOSED;
581		    vptr += 7;
582		  }
583		copyto (&vptr, &fptr, '/', (*(vptr-1) != ']'));
584		state = 9;
585	      }
586	    break;
587
588	  case 7:				/* add '.dir' and exit */
589	    if ((nstate == N_OPEN)
590		|| (nstate == N_DOT))
591	      {
592		s = vptr-1;
593		while (s > vmsname)
594		  {
595		    if (*s == ']')
596		      {
597			break;
598		      }
599		    if (*s == '.')
600		      {
601			*s = ']';
602			break;
603		      }
604		    s--;
605		  }
606	      }
607	    strcpy (vptr, ".dir");
608	    vptr += 4;
609	    state = -1;
610	    break;
611
612	  case 8:				/* add ']' and exit */
613	    *vptr++ = ']';
614	    state = -1;
615	    break;
616
617	  case 9:				/* 17..21, fptr -> 1st '/' + 1 */
618	    if (*fptr == 0)
619	      {
620		if (type == 2)
621		  {
622		    state = 7;
623		  }
624		else
625		  state = 8;
626		break;
627	      }
628	    s = strchr (fptr, '/');
629	    if (s == 0)
630	      {
631		if (type != 1)
632		  {
633		    if (nstate == N_OPEN)
634		      {
635			*vptr++ = ']';
636			nstate = N_CLOSED;
637		      }
638		    as_dir = 0;
639		  }
640		else
641		  {
642		    if (nstate == N_OPEN)
643		      {
644			*vptr++ = '.';
645			nstate = N_DOT;
646		      }
647		    as_dir = 1;
648		  }
649	      }
650	    else
651	      {
652		while (*(s+1) == '/')
653		  s++;
654		if ( (type == 2)
655		    && (*(s+1) == 0))		/* 19(2), 21(2)*/
656		  {
657		    if (nstate != N_CLOSED)
658		      {
659			*vptr++ = ']';
660			nstate = N_CLOSED;
661		      }
662		    as_dir = 1;
663		  }
664		else
665		  {
666		    if (nstate == N_OPEN)
667		      {
668			*vptr++ = '.';
669			nstate = N_DOT;
670		      }
671		    as_dir = 1;
672		  }
673	      }
674	    if ( (*fptr == '.')			/* check for '..' or '../' */
675		&& (*(fptr+1) == '.')
676		&& ((*(fptr+2) == '/')
677		    || (*(fptr+2) == 0)) )
678	      {
679		fptr += 2;
680		if (*fptr == '/')
681		  {
682		    do
683		      {
684			fptr++;
685		      }
686		    while (*fptr == '/');
687		  }
688		else if (*fptr == 0)
689		  type = 1;
690		vptr--;				/* vptr -> '.' or ']' */
691		s1 = vptr;
692		for (;;)
693		  {
694		    s1--;
695		    if (*s1 == '.')		/* one back */
696		      {
697			vptr = s1;
698			nstate = N_OPEN;
699			break;
700		      }
701		    if (*s1 == '[')		/* top level reached */
702		      {
703			if (*fptr == 0)
704			  {
705			    strcpy (s1, "[000000]");
706			    vptr = s1 + 8;
707			    nstate = N_CLOSED;
708			    s = 0;
709			    break;
710			  }
711			else
712			  {
713			    vptr = s1+1;
714			    nstate = N_OPEN;
715			    break;
716			  }
717		      }
718		  }
719	      }
720	    else
721	      {
722		copyto (&vptr, &fptr, '/', as_dir);
723		if (nstate == N_DOT)
724		  nstate = N_OPEN;
725	      }
726	    if (s == 0)
727	      {					/* 18,20 */
728		if (type == 1)
729		  *vptr++ = ']';
730		state = -1;
731	      }
732	    else
733	      {
734		if (*(s+1) == 0)
735		  {
736		    if (type == 2)		/* 19,21 */
737		      {
738		        state = 7;
739		      }
740		    else
741		      {
742			*vptr++ = ']';
743			state = -1;
744		      }
745		  }
746	      }
747	    break;
748
749	  case 10:				/* 1,2 first is '.' */
750	    if (*fptr == '.')
751	      {
752		fptr++;
753		state = 11;
754	      }
755	    else
756	      state = 12;
757	    break;
758
759	  case 11:				/* 2, '..' at start */
760	    count = 1;
761	    if (*fptr != 0)
762	      {
763		if (*fptr != '/')		/* got ..xxx */
764		  {
765		    return name;
766		  }
767		do				/* got ../ */
768		  {
769		    fptr++;
770		    while (*fptr == '/') fptr++;
771		    if (*fptr != '.')
772		      break;
773		    if (*(fptr+1) != '.')
774		      break;
775		    fptr += 2;
776		    if ((*fptr == 0)
777			|| (*fptr == '/'))
778		      count++;
779		  }
780		while (*fptr == '/');
781	      }
782	    {					/* got '..' or '../' */
783	      char cwdbuf[MAXPATHLEN+1];
784
785	      s1 = getcwd(cwdbuf, MAXPATHLEN);
786	      if (s1 == 0)
787		{
788		  return "";	    /* FIXME, err getcwd */
789		}
790	      strcpy (vptr, s1);
791	      s = strchr (vptr, ']');
792	      if (s != 0)
793		{
794		  nstate = N_OPEN;
795		  while (s > vptr)
796		    {
797		      s--;
798		      if (*s == '[')
799			{
800			  s++;
801			  strcpy (s, "000000]");
802			  state = -1;
803			  break;
804			}
805		      else if (*s == '.')
806			{
807			  if (--count == 0)
808			    {
809			      if (*fptr == 0)	/* had '..' or '../' */
810				{
811				  *s++ = ']';
812				  state = -1;
813				}
814			      else			/* had '../xxx' */
815				{
816				  state = 9;
817				}
818			      *s = 0;
819			      break;
820			    }
821			}
822		    }
823		}
824	      vptr += strlen (vptr);
825	    }
826	    break;
827
828	  case 12:				/* 1, '.' at start */
829	    if (*fptr != 0)
830	      {
831		if (*fptr != '/')
832		  {
833		    return name;
834		  }
835		while (*fptr == '/')
836		  fptr++;
837	      }
838
839	    {
840	      char cwdbuf[MAXPATHLEN+1];
841
842	      s1 = getcwd(cwdbuf, MAXPATHLEN);
843	      if (s1 == 0)
844		{
845		  return "";	    /*FIXME, err getcwd */
846		}
847	      strcpy (vptr, s1);
848	      if (*fptr == 0)
849		{
850		  state = -1;
851		  break;
852		}
853	      else
854		{
855		  s = strchr (vptr, ']');
856		  if (s == 0)
857		    {
858		      state = -1;
859		      break;
860		    }
861		  *s = 0;
862		  nstate = N_OPEN;
863		  vptr += strlen (vptr);
864		  state = 9;
865		}
866	    }
867	    break;
868	}
869
870	}
871      while (state > 0);
872
873
874    }
875
876
877  /* directory conversion done
878     fptr -> filename part of input string
879     vptr -> free space in vmsname
880  */
881
882  *vptr++ = 0;
883
884  return vmsname;
885}
886
887
888
889/*
890  convert from vms-style to unix-style
891
892  dev:[dir1.dir2]	//dev/dir1/dir2/
893*/
894
895char *
896unixify (char *name)
897{
898  static char piece[512];
899  char *s, *p;
900
901  if (strchr (name, '/') != 0)		/* already in unix style */
902    return name;
903
904  p = piece;
905  *p = 0;
906
907  /* device part */
908
909  s = strchr (name, ':');
910
911  if (s != 0)
912    {
913      *s = 0;
914      *p++ = '/';
915      *p++ = '/';
916      strcpy (p, name);
917      p += strlen (p);
918      *s = ':';
919    }
920
921  /* directory part */
922
923  *p++ = '/';
924  s = strchr (name, '[');
925
926  if (s != 0)
927    {
928      s++;
929      switch (*s)
930        {
931	  case ']':		/* [] */
932	    strcat (p, "./");
933	    break;
934	  case '-':		/* [- */
935	    strcat (p, "../");
936	    break;
937	  case '.':
938	    strcat (p, "./");	/* [. */
939	    break;
940	  default:
941	    s--;
942	    break;
943        }
944      s++;
945      while (*s)
946        {
947	  if (*s == '.')
948	    *p++ = '/';
949	  else
950	    *p++ = *s;
951	  s++;
952	  if (*s == ']')
953	    {
954	      s++;
955	      break;
956	    }
957        }
958      if (*s != 0)		/* more after ']' ?? */
959        {
960	  if (*(p-1) != '/')
961	    *p++ = '/';
962	  strcpy (p, s);		/* copy it anyway */
963        }
964    }
965
966  else		/* no '[' anywhere */
967
968    {
969      *p++ = 0;
970    }
971
972  /* force end with '/' */
973
974  if (*(p-1) != '/')
975    *p++ = '/';
976  *p = 0;
977
978  return piece;
979}
980
981/* EOF */
982