- Rework secondary expansion so we only defer it if there's a possibility
[make.git] / vmsify.c
blob23f0d5a75b9d6e3d3a272867d97f460391638fa5
1 /* vmsify.c -- Module for vms <-> unix file name conversion
2 Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
3 2007 Free Software Foundation, Inc.
4 This file is part of GNU Make.
6 GNU Make is free software; you can redistribute it and/or modify it under the
7 terms of the GNU General Public License as published by the Free Software
8 Foundation; either version 3 of the License, or (at your option) any later
9 version.
11 GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along with
16 this program. If not, see <http://www.gnu.org/licenses/>. */
18 /* Written by Klaus Kämpf (kkaempf@progis.de)
19 of proGIS Software, Aachen, Germany */
22 #include <stdio.h>
23 #include <string.h>
24 #include <ctype.h>
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. */
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)
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(). */
50 #define INIT_DSC_CSTRING(dsc, s) INIT_DSC_S(dsc, s, strlen(s))
51 #endif
54 copy 'from' to 'to' up to but not including 'upto'
55 return 0 if eos on from
56 return 1 if upto found
58 return 'to' at last char + 1
59 return 'from' at match + 1 or eos if no match
61 if as_dir == 1, change all '.' to '_'
62 else change all '.' but the last to '_'
65 static int
66 copyto (char **to, const char **from, char upto, int as_dir)
68 const char *s;
70 s = strrchr (*from, '.');
72 while (**from)
74 if (**from == upto)
78 (*from)++;
80 while (**from == upto);
81 return 1;
83 if (**from == '.')
85 if ((as_dir == 1)
86 || (*from != s))
87 **to = '_';
88 else
89 **to = '.';
91 else
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;
100 (*to)++;
101 (*from)++;
104 return 0;
109 get translation of logical name
113 static char *
114 trnlog (const char *name)
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;
123 INIT_DSC_CSTRING (name_dsc, name);
125 stat = lib$sys_trnlog (&name_dsc, &resltlen, &reslt_dsc);
127 if ((stat&1) == 0)
129 return "";
131 if (stat == SS$_NOTRAN)
133 return "";
135 reslt[resltlen] = '\0';
137 s = malloc (resltlen+1);
138 if (s == 0)
139 return "";
140 strcpy (s, reslt);
141 return s;
144 static char *
145 showall (char *s)
147 static char t[512];
148 char *pt;
150 pt = t;
151 if (strchr (s, '\\') == 0)
152 return s;
153 while (*s)
155 if (*s == '\\')
157 *pt++ = *s;
159 *pt++ = *s++;
161 return pt;
165 enum namestate { N_START, N_DEVICE, N_OPEN, N_DOT, N_CLOSED, N_DONE };
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
173 The following conversions are applied
174 (0) (1) (2)
175 input full name dir name file name
177 1 ./ <cwd> [] <current directory>.dir
178 2 ../ <home of cwd> <home of cwd> <home of cwd>.dir
180 3 // <dev of cwd>: <dev of cwd>:[000000] <dev of cwd>:000000.dir
181 4 //a a: a: a:
182 5 //a/ a: a: a:000000.dir
184 9 / [000000] [000000] 000000.dir
185 10 /a [000000]a [a] [000000]a
186 11 /a/ [a] [a] [000000]a.dir
187 12 /a/b [a]b [a.b] [a]b
188 13 /a/b/ [a.b] [a.b] [a]b.dir
189 14 /a/b/c [a.b]c [a.b.c] [a.b]c
190 15 /a/b/c/ [a.b.c] [a.b.c] [a.b]c.dir
192 16 a a [.a] a
193 17 a/ [.a] [.a] a.dir
194 18 a/b [.a]b [.a.b] [.a]b
195 19 a/b/ [.a.b] [.a.b] [.a]b.dir
196 20 a/b/c [.a.b]c [.a.b.c] [.a.b]c
197 21 a/b/c/ [.a.b.c] [.a.b.c] [.a.b]c.dir
199 22 a.b.c a_b.c [.a_b_c] a_b_c.dir
201 23 [x][y]z [x.y]z [x.y]z [x.y]z
202 24 [x][.y]z [x.y]z [x.y]z [x.y]z
204 25 filenames with '$' are left unchanged if they contain no '/'
205 25 filenames with ':' are left unchanged
206 26 filenames with a single pair of '[' ']' are left unchanged
208 The input string is not written to. The result is also const because
209 it's a static buffer; we don't want to change it.
212 const char *
213 vmsify (const char *name, int type)
215 /* max 255 device
216 max 39 directory
217 max 39 filename
218 max 39 filetype
219 max 5 version
221 #define MAXPATHLEN 512
223 enum namestate nstate;
224 static char vmsname[MAXPATHLEN+1];
225 const char *fptr;
226 const char *t;
227 char *vptr;
228 int as_dir;
229 int count;
231 if (name == 0)
232 return 0;
233 fptr = name;
234 vptr = vmsname;
235 nstate = N_START;
237 /* case 25a */
238 t = strpbrk (name, "$:");
240 if (t != 0)
242 const char *s1;
243 const char *s2;
245 if (type == 1)
247 s1 = strchr (t+1, '[');
248 s2 = strchr (t+1, ']');
251 if (*t == '$')
253 if (strchr (name, '/') == 0)
255 strcpy (vmsname, name);
256 if ((type == 1) && (s1 != 0) && (s2 == 0))
257 strcat (vmsname, "]");
258 return vmsname;
261 else
263 strcpy (vmsname, name);
264 if ((type == 1) && (s1 != 0) && (s2 == 0))
265 strcat (vmsname, "]");
266 return vmsname;
270 /* case 26 */
271 t = strchr (name, '[');
273 if (t != 0)
275 const char *s;
276 const char *s1 = strchr (t+1, '[');
277 if (s1 == 0)
279 strcpy (vmsname, name);
280 if ((type == 1) && (strchr (t+1, ']') == 0))
281 strcat (vmsname, "]");
282 return vmsname;
284 s1--;
285 if (*s1 != ']')
287 strcpy (vmsname, name);
288 return vmsname; /* not ][, keep unchanged */
291 /* we have ][ */
293 s = name;
295 /* s -> starting char
296 s1 -> ending ']' */
299 strncpy (vptr, s, s1-s); /* copy up to but not including ']' */
300 vptr += s1-s;
301 if (*s1 == 0)
302 break;
303 s = s1 + 1; /* s -> char behind ']' */
304 if (*s != '[') /* was '][' ? */
305 break; /* no, last ] found, exit */
306 s++;
307 if (*s != '.')
308 *vptr++ = '.';
309 s1 = strchr (s, ']');
310 if (s1 == 0) /* no closing ] */
311 s1 = s + strlen (s);
313 while (1);
315 *vptr++ = ']';
317 fptr = s;
320 else /* no [ in name */
322 int state = 0;
323 int rooted = 1; /* flag if logical is rooted, else insert [000000] */
327 switch (state)
329 case 0: /* start of loop */
330 if (*fptr == '/')
332 fptr++;
333 state = 1;
335 else if (*fptr == '.')
337 fptr++;
338 state = 10;
340 else
341 state = 2;
342 break;
344 case 1: /* '/' at start */
345 if (*fptr == '/')
347 fptr++;
348 state = 3;
350 else
351 state = 4;
352 break;
354 case 2: /* no '/' at start */
356 const char *s = strchr (fptr, '/');
357 if (s == 0) /* no '/' (16) */
359 if (type == 1)
361 strcpy (vptr, "[.");
362 vptr += 2;
364 copyto (&vptr, &fptr, 0, (type==1));
365 if (type == 1)
366 *vptr++ = ']';
367 state = -1;
369 else /* found '/' (17..21) */
371 if ((type == 2)
372 && (*(s+1) == 0)) /* 17(2) */
374 copyto (&vptr, &fptr, '/', 1);
375 state = 7;
377 else
379 strcpy (vptr, "[.");
380 vptr += 2;
381 copyto (&vptr, &fptr, '/', 1);
382 nstate = N_OPEN;
383 state = 9;
386 break;
389 case 3: /* '//' at start */
391 const char *s;
392 const char *s1;
393 char *vp;
394 while (*fptr == '/') /* collapse all '/' */
395 fptr++;
396 if (*fptr == 0) /* just // */
398 char cwdbuf[MAXPATHLEN+1];
400 s1 = getcwd(cwdbuf, MAXPATHLEN);
401 if (s1 == 0)
403 vmsname[0] = '\0';
404 return vmsname; /* FIXME, err getcwd */
406 s = strchr (s1, ':');
407 if (s == 0)
409 vmsname[0] = '\0';
410 return vmsname; /* FIXME, err no device */
412 strncpy (vptr, s1, s-s1+1);
413 vptr += s-s1+1;
414 state = -1;
415 break;
418 s = vptr;
420 if (copyto (&vptr, &fptr, '/', 1) == 0) /* copy device part */
422 *vptr++ = ':';
423 state = -1;
424 break;
426 *vptr = ':';
427 nstate = N_DEVICE;
428 if (*fptr == 0) /* just '//a/' */
430 strcpy (vptr+1, "[000000]");
431 vptr += 9;
432 state = -1;
433 break;
435 *vptr = 0;
436 /* check logical for [000000] insertion */
437 vp = trnlog (s);
438 if (*vp != '\0')
439 { /* found translation */
440 for (;;) /* loop over all nested logicals */
442 char *vp2 = vp + strlen (vp) - 1;
443 if (*vp2 == ':') /* translation ends in ':' */
445 vp2 = trnlog (vp);
446 free (vp);
447 if (*vp2 == 0)
449 rooted = 0;
450 break;
452 vp = vp2;
453 continue; /* next iteration */
455 if (*vp2 == ']') /* translation ends in ']' */
457 if (*(vp2-1) == '.') /* ends in '.]' */
459 if (strncmp (fptr, "000000", 6) != 0)
460 rooted = 0;
462 else
464 strcpy (vmsname, s1);
465 vp = strchr (vmsname, ']');
466 *vp = '.';
467 nstate = N_DOT;
468 vptr = vp;
471 break;
473 free (vp);
475 else
476 rooted = 0;
478 if (*vptr == 0)
480 nstate = N_DEVICE;
481 *vptr++ = ':';
483 else
484 vptr++;
486 if (rooted == 0)
488 nstate = N_DOT;
489 strcpy (vptr, "[000000.");
490 vptr += 8;
491 vp = vptr-1;
493 else
494 vp = 0;
496 /* vp-> '.' after 000000 or NULL */
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;
509 else
511 while (*(s+1) == '/') /* skip multiple '/' */
512 s++;
515 if ((rooted != 0)
516 && (*(vptr-1) != '.'))
518 *vptr++ = '[';
519 nstate = N_DOT;
521 else
522 if ((nstate == N_DOT)
523 && (vp != 0)
524 && (*(s+1) == 0))
526 if (type == 2)
528 *vp = ']';
529 nstate = N_CLOSED;
532 state = 9;
533 break;
535 case 4: /* single '/' at start (9..15) */
536 if (*fptr == 0)
537 state = 5;
538 else
539 state = 6;
540 break;
542 case 5: /* just '/' at start (9) */
543 if (type != 2)
545 *vptr++ = '[';
546 nstate = N_OPEN;
548 strcpy (vptr, "000000");
549 vptr += 6;
550 if (type == 2)
551 state = 7;
552 else
553 state = 8;
554 break;
556 case 6: /* chars following '/' at start 10..15 */
558 const char *s;
559 *vptr++ = '[';
560 nstate = N_OPEN;
561 s = strchr (fptr, '/');
562 if (s == 0) /* 10 */
564 if (type != 1)
566 strcpy (vptr, "000000]");
567 vptr += 7;
569 copyto (&vptr, &fptr, 0, (type == 1));
570 if (type == 1)
572 *vptr++ = ']';
574 state = -1;
576 else /* 11..15 */
578 if ( (type == 2)
579 && (*(s+1) == 0)) /* 11(2) */
581 strcpy (vptr, "000000]");
582 nstate = N_CLOSED;
583 vptr += 7;
585 copyto (&vptr, &fptr, '/', (*(vptr-1) != ']'));
586 state = 9;
588 break;
591 case 7: /* add '.dir' and exit */
592 if ((nstate == N_OPEN)
593 || (nstate == N_DOT))
595 char *vp = vptr-1;
596 while (vp > vmsname)
598 if (*vp == ']')
600 break;
602 if (*vp == '.')
604 *vp = ']';
605 break;
607 vp--;
610 strcpy (vptr, ".dir");
611 vptr += 4;
612 state = -1;
613 break;
615 case 8: /* add ']' and exit */
616 *vptr++ = ']';
617 state = -1;
618 break;
620 case 9: /* 17..21, fptr -> 1st '/' + 1 */
622 const char *s;
623 if (*fptr == 0)
625 if (type == 2)
627 state = 7;
629 else
630 state = 8;
631 break;
633 s = strchr (fptr, '/');
634 if (s == 0)
636 if (type != 1)
638 if (nstate == N_OPEN)
640 *vptr++ = ']';
641 nstate = N_CLOSED;
643 as_dir = 0;
645 else
647 if (nstate == N_OPEN)
649 *vptr++ = '.';
650 nstate = N_DOT;
652 as_dir = 1;
655 else
657 while (*(s+1) == '/')
658 s++;
659 if ( (type == 2)
660 && (*(s+1) == 0)) /* 19(2), 21(2)*/
662 if (nstate != N_CLOSED)
664 *vptr++ = ']';
665 nstate = N_CLOSED;
667 as_dir = 1;
669 else
671 if (nstate == N_OPEN)
673 *vptr++ = '.';
674 nstate = N_DOT;
676 as_dir = 1;
679 if ( (*fptr == '.') /* check for '..' or '../' */
680 && (*(fptr+1) == '.')
681 && ((*(fptr+2) == '/')
682 || (*(fptr+2) == 0)) )
684 char *vp;
685 fptr += 2;
686 if (*fptr == '/')
690 fptr++;
692 while (*fptr == '/');
694 else if (*fptr == 0)
695 type = 1;
696 vptr--; /* vptr -> '.' or ']' */
697 vp = vptr;
698 for (;;)
700 vp--;
701 if (*vp == '.') /* one back */
703 vptr = vp;
704 nstate = N_OPEN;
705 break;
707 if (*vp == '[') /* top level reached */
709 if (*fptr == 0)
711 strcpy (vp, "[000000]");
712 vptr = vp + 8;
713 nstate = N_CLOSED;
714 s = 0;
715 break;
717 else
719 vptr = vp+1;
720 nstate = N_OPEN;
721 break;
726 else
728 copyto (&vptr, &fptr, '/', as_dir);
729 if (nstate == N_DOT)
730 nstate = N_OPEN;
732 if (s == 0)
733 { /* 18,20 */
734 if (type == 1)
735 *vptr++ = ']';
736 state = -1;
738 else
740 if (*(s+1) == 0)
742 if (type == 2) /* 19,21 */
744 state = 7;
746 else
748 *vptr++ = ']';
749 state = -1;
753 break;
756 case 10: /* 1,2 first is '.' */
757 if (*fptr == '.')
759 fptr++;
760 state = 11;
762 else
763 state = 12;
764 break;
766 case 11: /* 2, '..' at start */
767 count = 1;
768 if (*fptr != 0)
770 if (*fptr != '/') /* got ..xxx */
772 strcpy (vmsname, name);
773 return vmsname;
775 do /* got ../ */
777 fptr++;
778 while (*fptr == '/') fptr++;
779 if (*fptr != '.')
780 break;
781 if (*(fptr+1) != '.')
782 break;
783 fptr += 2;
784 if ((*fptr == 0)
785 || (*fptr == '/'))
786 count++;
788 while (*fptr == '/');
790 { /* got '..' or '../' */
791 char *vp;
792 char cwdbuf[MAXPATHLEN+1];
794 vp = getcwd(cwdbuf, MAXPATHLEN);
795 if (vp == 0)
797 vmsname[0] = '\0';
798 return vmsname; /* FIXME, err getcwd */
800 strcpy (vptr, vp);
801 vp = strchr (vptr, ']');
802 if (vp != 0)
804 nstate = N_OPEN;
805 while (vp > vptr)
807 vp--;
808 if (*vp == '[')
810 vp++;
811 strcpy (vp, "000000]");
812 state = -1;
813 break;
815 else if (*vp == '.')
817 if (--count == 0)
819 if (*fptr == 0) /* had '..' or '../' */
821 *vp++ = ']';
822 state = -1;
824 else /* had '../xxx' */
826 state = 9;
828 *vp = '\0';
829 break;
834 vptr += strlen (vptr);
836 break;
838 case 12: /* 1, '.' at start */
839 if (*fptr != 0)
841 if (*fptr != '/')
843 strcpy (vmsname, name);
844 return vmsname;
846 while (*fptr == '/')
847 fptr++;
851 char *vp;
852 char cwdbuf[MAXPATHLEN+1];
854 vp = getcwd(cwdbuf, MAXPATHLEN);
855 if (vp == 0)
857 vmsname[0] = '\0';
858 return vmsname; /*FIXME, err getcwd */
860 strcpy (vptr, vp);
862 if (*fptr == 0)
864 state = -1;
865 break;
867 else
869 char *vp = strchr (vptr, ']');
870 if (vp == 0)
872 state = -1;
873 break;
875 *vp = '\0';
876 nstate = N_OPEN;
877 vptr += strlen (vptr);
878 state = 9;
880 break;
884 while (state > 0);
890 /* directory conversion done
891 fptr -> filename part of input string
892 vptr -> free space in vmsname
895 *vptr++ = 0;
897 return vmsname;
903 convert from vms-style to unix-style
905 dev:[dir1.dir2] //dev/dir1/dir2/
908 const char *
909 unixify (const char *name)
911 static char piece[512];
912 const char *s;
913 char *p;
915 if (strchr (name, '/') != 0) /* already in unix style */
917 strcpy (piece, name);
918 return piece;
921 p = piece;
922 *p = 0;
924 /* device part */
926 s = strchr (name, ':');
928 if (s != 0)
930 int l = s - name;
931 *p++ = '/';
932 *p++ = '/';
933 strncpy (p, name, l);
934 p += l;
937 /* directory part */
939 *p++ = '/';
940 s = strchr (name, '[');
942 if (s != 0)
944 s++;
945 switch (*s)
947 case ']': /* [] */
948 strcat (p, "./");
949 break;
950 case '-': /* [- */
951 strcat (p, "../");
952 break;
953 case '.':
954 strcat (p, "./"); /* [. */
955 break;
956 default:
957 s--;
958 break;
960 s++;
961 while (*s)
963 if (*s == '.')
964 *p++ = '/';
965 else
966 *p++ = *s;
967 s++;
968 if (*s == ']')
970 s++;
971 break;
974 if (*s != 0) /* more after ']' ?? */
976 if (*(p-1) != '/')
977 *p++ = '/';
978 strcpy (p, s); /* copy it anyway */
982 else /* no '[' anywhere */
985 *p++ = 0;
988 /* force end with '/' */
990 if (*(p-1) != '/')
991 *p++ = '/';
992 *p = 0;
994 return piece;
997 /* EOF */