1 /* vmsify.c -- Module for vms <-> unix file name conversion
2 Copyright (C) 1996-2022 Free Software Foundation, Inc.
3 This file is part of GNU Make.
5 GNU Make is free software; you can redistribute it and/or modify it under the
6 terms of the GNU General Public License as published by the Free Software
7 Foundation; either version 3 of the License, or (at your option) any later
10 GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
11 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License along with
15 this program. If not, see <https://www.gnu.org/licenses/>. */
17 /* Written by Klaus Kämpf (kkaempf@progis.de)
18 of proGIS Software, Aachen, Germany */
35 #include <lib$routines.h>
36 /* Initialize a string descriptor (struct dsc$descriptor_s) for an
37 arbitrary string. ADDR is a pointer to the first character
38 of the string, and LEN is the length of the string. */
40 #define INIT_DSC_S(dsc, addr, len) do { \
41 (dsc).dsc$b_dtype = DSC$K_DTYPE_T; \
42 (dsc).dsc$b_class = DSC$K_CLASS_S; \
43 (dsc).dsc$w_length = (len); \
44 (dsc).dsc$a_pointer = (addr); \
47 /* Initialize a string descriptor (struct dsc$descriptor_s) for a
48 NUL-terminated string. S is a pointer to the string; the length
49 is determined by calling strlen(). */
51 #define INIT_DSC_CSTRING(dsc, s) INIT_DSC_S(dsc, s, strlen(s))
55 copy 'from' to 'to' up to but not including 'upto'
56 return 0 if eos on from
57 return 1 if upto found
59 return 'to' at last char + 1
60 return 'from' at match + 1 or eos if no match
62 if as_dir == 1, change all '.' to '_'
63 else change all '.' but the last to '_'
67 copyto (char **to, const char **from, char upto, int as_dir)
71 s = strrchr (*from, '.');
81 while (**from == upto);
94 #ifdef HAVE_CASE_INSENSITIVE_FS
95 if (isupper ((unsigned char)**from))
96 **to = tolower ((unsigned char)**from);
110 get translation of logical name
115 trnlog (const char *name)
118 static char reslt[1024];
119 $DESCRIPTOR (reslt_dsc, reslt);
121 struct dsc$descriptor_s name_dsc;
125 * the string isn't changed, but there is no string descriptor with
126 * "const char *dsc$a_pointer"
128 INIT_DSC_CSTRING (name_dsc, (char *)name);
130 stat = lib$sys_trnlog (&name_dsc, &resltlen, &reslt_dsc);
136 if (stat == SS$_NOTRAN)
140 reslt[resltlen] = '\0';
142 s = xmalloc (resltlen+1);
154 if (strchr (s, '\\') == 0)
168 enum namestate { N_START, N_DEVICE, N_OPEN, N_DOT, N_CLOSED, N_DONE };
171 convert unix style name to vms style
172 type = 0 -> name is a full name (directory and filename part)
173 type = 1 -> name is a directory
174 type = 2 -> name is a filename without directory
176 The following conversions are applied
178 input full name dir name file name
180 1 ./ <cwd> [] <current directory>.dir
181 2 ../ <home of cwd> <home of cwd> <home of cwd>.dir
183 3 // <dev of cwd>: <dev of cwd>:[000000] <dev of cwd>:000000.dir
185 5 //a/ a: a: a:000000.dir
187 9 / [000000] [000000] 000000.dir
188 10 /a [000000]a [a] [000000]a
189 11 /a/ [a] [a] [000000]a.dir
190 12 /a/b [a]b [a.b] [a]b
191 13 /a/b/ [a.b] [a.b] [a]b.dir
192 14 /a/b/c [a.b]c [a.b.c] [a.b]c
193 15 /a/b/c/ [a.b.c] [a.b.c] [a.b]c.dir
196 17 a/ [.a] [.a] a.dir
197 18 a/b [.a]b [.a.b] [.a]b
198 19 a/b/ [.a.b] [.a.b] [.a]b.dir
199 20 a/b/c [.a.b]c [.a.b.c] [.a.b]c
200 21 a/b/c/ [.a.b.c] [.a.b.c] [.a.b]c.dir
202 22 a.b.c a_b.c [.a_b_c] a_b_c.dir
204 23 [x][y]z [x.y]z [x.y]z [x.y]z
205 24 [x][.y]z [x.y]z [x.y]z [x.y]z
207 25 filenames with '$' are left unchanged if they contain no '/'
208 25 filenames with ':' are left unchanged
209 26 filenames with a single pair of '[' ']' are left unchanged
211 The input string is not written to. The result is also const because
212 it's a static buffer; we don't want to change it.
216 vmsify (const char *name, int type)
224 /* todo: VMSMAXPATHLEN is defined for ODS2 names: it needs to be adjusted. */
225 #define VMSMAXPATHLEN 512
227 enum namestate nstate;
228 static char vmsname[VMSMAXPATHLEN+1];
245 t = strpbrk (name, "$:");
254 s1 = strchr (t+1, '[');
255 s2 = strchr (t+1, ']');
260 if (strchr (name, '/') == 0)
262 strcpy (vmsname, name);
263 if ((type == 1) && (s1 != 0) && (s2 == 0))
264 strcat (vmsname, "]");
270 strcpy (vmsname, name);
271 if ((type == 1) && (s1 != 0) && (s2 == 0))
272 strcat (vmsname, "]");
278 t = strchr (name, '[');
283 // const char *s1 = strchr (t+1, '[');
284 s1 = strchr (t+1, '[');
287 strcpy (vmsname, name);
288 if ((type == 1) && (strchr (t+1, ']') == 0))
289 strcat (vmsname, "]");
295 strcpy (vmsname, name);
296 return vmsname; /* not ][, keep unchanged */
303 /* s -> starting char
307 strncpy (vptr, s, s1-s); /* copy up to but not including ']' */
311 s = s1 + 1; /* s -> char behind ']' */
312 if (*s != '[') /* was '][' ? */
313 break; /* no, last ] found, exit */
317 s1 = strchr (s, ']');
318 if (s1 == 0) /* no closing ] */
328 else /* no [ in name */
331 int rooted = 1; /* flag if logical is rooted, else insert [000000] */
337 case 0: /* start of loop */
343 else if (*fptr == '.')
352 case 1: /* '/' at start */
362 case 2: /* no '/' at start */
364 const char *s = strchr (fptr, '/');
365 if (s == 0) /* no '/' (16) */
372 copyto (&vptr, &fptr, 0, (type==1));
377 else /* found '/' (17..21) */
380 && (*(s+1) == 0)) /* 17(2) */
382 copyto (&vptr, &fptr, '/', 1);
389 copyto (&vptr, &fptr, '/', 1);
397 case 3: /* '//' at start */
402 while (*fptr == '/') /* collapse all '/' */
404 if (*fptr == 0) /* just // */
406 char cwdbuf[VMSMAXPATHLEN+1];
408 s1 = getcwd(cwdbuf, VMSMAXPATHLEN);
412 return vmsname; /* FIXME, err getcwd */
414 s = strchr (s1, ':');
418 return vmsname; /* FIXME, err no device */
420 strncpy (vptr, s1, s-s1+1);
428 if (copyto (&vptr, &fptr, '/', 1) == 0) /* copy device part */
436 if (*fptr == 0) /* just '//a/' */
438 strcpy (vptr+1, "[000000]");
444 /* check logical for [000000] insertion */
447 { /* found translation */
448 for (;;) /* loop over all nested logicals */
450 char *vp2 = vp + strlen (vp) - 1;
451 if (*vp2 == ':') /* translation ends in ':' */
461 continue; /* next iteration */
463 if (*vp2 == ']') /* translation ends in ']' */
465 if (*(vp2-1) == '.') /* ends in '.]' */
467 if (strncmp (fptr, "000000", 6) != 0)
472 strcpy (vmsname, s1);
473 vp = strchr (vmsname, ']');
497 strcpy (vptr, "[000000.");
504 /* vp-> '.' after 000000 or NULL */
506 s = strchr (fptr, '/');
509 if (*(vptr-1) == '.')
511 else if (rooted == 0)
513 copyto (&vptr, &fptr, 0, (type == 1));
519 while (*(s+1) == '/') /* skip multiple '/' */
524 && (*(vptr-1) != '.'))
530 if ((nstate == N_DOT)
543 case 4: /* single '/' at start (9..15) */
550 case 5: /* just '/' at start (9) */
556 strcpy (vptr, "000000");
564 case 6: /* chars following '/' at start 10..15 */
569 s = strchr (fptr, '/');
574 strcpy (vptr, "000000]");
577 copyto (&vptr, &fptr, 0, (type == 1));
587 && (*(s+1) == 0)) /* 11(2) */
589 strcpy (vptr, "000000]");
593 copyto (&vptr, &fptr, '/', (*(vptr-1) != ']'));
599 case 7: /* add '.dir' and exit */
600 if ((nstate == N_OPEN)
601 || (nstate == N_DOT))
618 strcpy (vptr, ".dir");
623 case 8: /* add ']' and exit */
628 case 9: /* 17..21, fptr -> 1st '/' + 1 */
641 s = strchr (fptr, '/');
646 if (nstate == N_OPEN)
655 if (nstate == N_OPEN)
665 while (*(s+1) == '/')
668 && (*(s+1) == 0)) /* 19(2), 21(2)*/
670 if (nstate != N_CLOSED)
679 if (nstate == N_OPEN)
687 if ( (*fptr == '.') /* check for '..' or '../' */
688 && (*(fptr+1) == '.')
689 && ((*(fptr+2) == '/')
690 || (*(fptr+2) == 0)) )
700 while (*fptr == '/');
704 vptr--; /* vptr -> '.' or ']' */
709 if (*vp == '.') /* one back */
715 if (*vp == '[') /* top level reached */
719 strcpy (vp, "[000000]");
736 copyto (&vptr, &fptr, '/', as_dir);
750 if (type == 2) /* 19,21 */
764 case 10: /* 1,2 first is '.' */
774 case 11: /* 2, '..' at start */
778 if (*fptr != '/') /* got ..xxx */
780 strcpy (vmsname, name);
786 while (*fptr == '/') fptr++;
789 if (*(fptr+1) != '.')
796 while (*fptr == '/');
798 { /* got '..' or '../' */
800 char cwdbuf[VMSMAXPATHLEN+1];
802 vp = getcwd(cwdbuf, VMSMAXPATHLEN);
806 return vmsname; /* FIXME, err getcwd */
809 vp = strchr (vptr, ']');
819 strcpy (vp, "000000]");
827 if (*fptr == 0) /* had '..' or '../' */
832 else /* had '../xxx' */
842 vptr += strlen (vptr);
846 case 12: /* 1, '.' at start */
851 strcpy (vmsname, name);
860 char cwdbuf[VMSMAXPATHLEN+1];
862 vp = getcwd(cwdbuf, VMSMAXPATHLEN);
866 return vmsname; /*FIXME, err getcwd */
877 char *vp = strchr (vptr, ']');
885 vptr += strlen (vptr);
898 /* directory conversion done
899 fptr -> filename part of input string
900 vptr -> free space in vmsname
911 convert from vms-style to unix-style
913 dev:[dir1.dir2] //dev/dir1/dir2/
917 unixify (const char *name)
919 static char piece[512];
923 if (strchr (name, '/') != 0) /* already in unix style */
925 strcpy (piece, name);
934 s = strchr (name, ':');
941 strncpy (p, name, l);
948 s = strchr (name, '[');
962 strcat (p, "./"); /* [. */
982 if (*s != 0) /* more after ']' ?? */
986 strcpy (p, s); /* copy it anyway */
990 else /* no '[' anywhere */
996 /* force end with '/' */