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