| /* vmsify.c -- Module for vms <-> unix file name conversion | |
| Copyright (C) 1996-2025 Free Software Foundation, Inc. | |
| This file is part of GNU Make. | |
| GNU Make is free software; you can redistribute it and/or modify it under the | |
| terms of the GNU General Public License as published by the Free Software | |
| Foundation; either version 3 of the License, or (at your option) any later | |
| version. | |
| GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY | |
| WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | |
| A PARTICULAR PURPOSE. See the GNU General Public License for more details. | |
| You should have received a copy of the GNU General Public License along with | |
| this program. If not, see <https://www.gnu.org/licenses/>. */ | |
| /* Written by Klaus Kämpf (kkaempf@progis.de) | |
| of proGIS Software, Aachen, Germany */ | |
| #include <stdio.h> | |
| #include <string.h> | |
| #include <ctype.h> | |
| #include "makeint.h" | |
| #if MK_OS_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, const char **from, char upto, int as_dir) | |
| { | |
| const 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 | |
| { | |
| #ifdef HAVE_CASE_INSENSITIVE_FS | |
| if (isupper ((unsigned char)**from)) | |
| **to = tolower ((unsigned char)**from); | |
| else | |
| #endif | |
| **to = **from; | |
| } | |
| (*to)++; | |
| (*from)++; | |
| } | |
| return 0; | |
| } | |
| /* | |
| get translation of logical name | |
| */ | |
| static char * | |
| trnlog (const char *name) | |
| { | |
| int stat; | |
| static char reslt[1024]; | |
| $DESCRIPTOR (reslt_dsc, reslt); | |
| short resltlen; | |
| struct dsc$descriptor_s name_dsc; | |
| char *s; | |
| /* | |
| * the string isn't changed, but there is no string descriptor with | |
| * "const char *dsc$a_pointer" | |
| */ | |
| INIT_DSC_CSTRING (name_dsc, (char *)name); | |
| stat = lib$sys_trnlog (&name_dsc, &resltlen, &reslt_dsc); | |
| if ((stat&1) == 0) | |
| { | |
| return ""; | |
| } | |
| if (stat == SS$_NOTRAN) | |
| { | |
| return ""; | |
| } | |
| reslt[resltlen] = '\0'; | |
| s = xmalloc (resltlen+1); | |
| strcpy (s, reslt); | |
| return s; | |
| } | |
| static char * | |
| showall (char *s) | |
| { | |
| static char t[512]; | |
| char *pt; | |
| pt = t; | |
| if (strchr (s, '\\') == 0) | |
| return s; | |
| while (*s) | |
| { | |
| if (*s == '\\') | |
| { | |
| *pt++ = *s; | |
| } | |
| *pt++ = *s++; | |
| } | |
| return pt; | |
| } | |
| 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. The result is also const because | |
| it's a static buffer; we don't want to change it. | |
| */ | |
| const char * | |
| vmsify (const char *name, int type) | |
| { | |
| /* max 255 device | |
| max 39 directory | |
| max 39 filename | |
| max 39 filetype | |
| max 5 version | |
| */ | |
| /* todo: VMSMAXPATHLEN is defined for ODS2 names: it needs to be adjusted. */ | |
| #define VMSMAXPATHLEN 512 | |
| enum namestate nstate; | |
| static char vmsname[VMSMAXPATHLEN+1]; | |
| const char *fptr; | |
| const char *t; | |
| char *vptr; | |
| int as_dir; | |
| int count; | |
| const char *s; | |
| const char *s1; | |
| const char *s2; | |
| if (name == 0) | |
| return 0; | |
| fptr = name; | |
| vptr = vmsname; | |
| nstate = N_START; | |
| /* case 25a */ | |
| t = strpbrk (name, "$:"); | |
| if (t != 0) | |
| { | |
| // const char *s1; | |
| // const char *s2; | |
| if (type == 1) | |
| { | |
| s1 = strchr (t+1, '['); | |
| s2 = strchr (t+1, ']'); | |
| } | |
| if (*t == '$') | |
| { | |
| if (strchr (name, '/') == 0) | |
| { | |
| strcpy (vmsname, name); | |
| if ((type == 1) && (s1 != 0) && (s2 == 0)) | |
| strcat (vmsname, "]"); | |
| return vmsname; | |
| } | |
| } | |
| else | |
| { | |
| strcpy (vmsname, name); | |
| if ((type == 1) && (s1 != 0) && (s2 == 0)) | |
| strcat (vmsname, "]"); | |
| return vmsname; | |
| } | |
| } | |
| /* case 26 */ | |
| t = strchr (name, '['); | |
| if (t != 0) | |
| { | |
| // const char *s; | |
| // const char *s1 = strchr (t+1, '['); | |
| s1 = strchr (t+1, '['); | |
| if (s1 == 0) | |
| { | |
| strcpy (vmsname, name); | |
| if ((type == 1) && (strchr (t+1, ']') == 0)) | |
| strcat (vmsname, "]"); | |
| return vmsname; | |
| } | |
| s1--; | |
| if (*s1 != ']') | |
| { | |
| strcpy (vmsname, name); | |
| return vmsname; /* 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 = 0; | |
| int rooted = 1; /* flag if logical is rooted, else insert [000000] */ | |
| 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 */ | |
| { | |
| const char *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 */ | |
| { | |
| // const char *s; | |
| // const char *s1; | |
| char *vp; | |
| while (*fptr == '/') /* collapse all '/' */ | |
| fptr++; | |
| if (*fptr == 0) /* just // */ | |
| { | |
| char cwdbuf[VMSMAXPATHLEN+1]; | |
| s1 = getcwd(cwdbuf, VMSMAXPATHLEN); | |
| if (s1 == 0) | |
| { | |
| vmsname[0] = '\0'; | |
| return vmsname; /* FIXME, err getcwd */ | |
| } | |
| s = strchr (s1, ':'); | |
| if (s == 0) | |
| { | |
| vmsname[0] = '\0'; | |
| return vmsname; /* 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 */ | |
| vp = trnlog (s); | |
| if (*vp != '\0') | |
| { /* found translation */ | |
| for (;;) /* loop over all nested logicals */ | |
| { | |
| char *vp2 = vp + strlen (vp) - 1; | |
| if (*vp2 == ':') /* translation ends in ':' */ | |
| { | |
| vp2 = trnlog (vp); | |
| free (vp); | |
| if (*vp2 == 0) | |
| { | |
| rooted = 0; | |
| break; | |
| } | |
| vp = vp2; | |
| continue; /* next iteration */ | |
| } | |
| if (*vp2 == ']') /* translation ends in ']' */ | |
| { | |
| if (*(vp2-1) == '.') /* ends in '.]' */ | |
| { | |
| if (strncmp (fptr, "000000", 6) != 0) | |
| rooted = 0; | |
| } | |
| else | |
| { | |
| strcpy (vmsname, s1); | |
| vp = strchr (vmsname, ']'); | |
| *vp = '.'; | |
| nstate = N_DOT; | |
| vptr = vp; | |
| } | |
| } | |
| break; | |
| } | |
| free (vp); | |
| } | |
| else | |
| rooted = 0; | |
| if (*vptr == 0) | |
| { | |
| nstate = N_DEVICE; | |
| *vptr++ = ':'; | |
| } | |
| else | |
| vptr++; | |
| if (rooted == 0) | |
| { | |
| nstate = N_DOT; | |
| strcpy (vptr, "[000000."); | |
| vptr += 8; | |
| vp = vptr-1; | |
| } | |
| else | |
| vp = 0; | |
| /* vp-> '.' 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) | |
| && (vp != 0) | |
| && (*(s+1) == 0)) | |
| { | |
| if (type == 2) | |
| { | |
| *vp = ']'; | |
| 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 */ | |
| { | |
| const char *s; | |
| *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)) | |
| { | |
| char *vp = vptr-1; | |
| while (vp > vmsname) | |
| { | |
| if (*vp == ']') | |
| { | |
| break; | |
| } | |
| if (*vp == '.') | |
| { | |
| *vp = ']'; | |
| break; | |
| } | |
| vp--; | |
| } | |
| } | |
| strcpy (vptr, ".dir"); | |
| vptr += 4; | |
| state = -1; | |
| break; | |
| case 8: /* add ']' and exit */ | |
| *vptr++ = ']'; | |
| state = -1; | |
| break; | |
| case 9: /* 17..21, fptr -> 1st '/' + 1 */ | |
| { | |
| const char *s; | |
| 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)) ) | |
| { | |
| char *vp; | |
| fptr += 2; | |
| if (*fptr == '/') | |
| { | |
| do | |
| { | |
| fptr++; | |
| } | |
| while (*fptr == '/'); | |
| } | |
| else if (*fptr == 0) | |
| type = 1; | |
| vptr--; /* vptr -> '.' or ']' */ | |
| vp = vptr; | |
| for (;;) | |
| { | |
| vp--; | |
| if (*vp == '.') /* one back */ | |
| { | |
| vptr = vp; | |
| nstate = N_OPEN; | |
| break; | |
| } | |
| if (*vp == '[') /* top level reached */ | |
| { | |
| if (*fptr == 0) | |
| { | |
| strcpy (vp, "[000000]"); | |
| vptr = vp + 8; | |
| nstate = N_CLOSED; | |
| s = 0; | |
| break; | |
| } | |
| else | |
| { | |
| vptr = vp+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 */ | |
| { | |
| strcpy (vmsname, name); | |
| return vmsname; | |
| } | |
| do /* got ../ */ | |
| { | |
| fptr++; | |
| while (*fptr == '/') fptr++; | |
| if (*fptr != '.') | |
| break; | |
| if (*(fptr+1) != '.') | |
| break; | |
| fptr += 2; | |
| if ((*fptr == 0) | |
| || (*fptr == '/')) | |
| count++; | |
| } | |
| while (*fptr == '/'); | |
| } | |
| { /* got '..' or '../' */ | |
| char *vp; | |
| char cwdbuf[VMSMAXPATHLEN+1]; | |
| vp = getcwd(cwdbuf, VMSMAXPATHLEN); | |
| if (vp == 0) | |
| { | |
| vmsname[0] = '\0'; | |
| return vmsname; /* FIXME, err getcwd */ | |
| } | |
| strcpy (vptr, vp); | |
| vp = strchr (vptr, ']'); | |
| if (vp != 0) | |
| { | |
| nstate = N_OPEN; | |
| while (vp > vptr) | |
| { | |
| vp--; | |
| if (*vp == '[') | |
| { | |
| vp++; | |
| strcpy (vp, "000000]"); | |
| state = -1; | |
| break; | |
| } | |
| else if (*vp == '.') | |
| { | |
| if (--count == 0) | |
| { | |
| if (*fptr == 0) /* had '..' or '../' */ | |
| { | |
| *vp++ = ']'; | |
| state = -1; | |
| } | |
| else /* had '../xxx' */ | |
| { | |
| state = 9; | |
| } | |
| *vp = '\0'; | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| vptr += strlen (vptr); | |
| } | |
| break; | |
| case 12: /* 1, '.' at start */ | |
| if (*fptr != 0) | |
| { | |
| if (*fptr != '/') | |
| { | |
| strcpy (vmsname, name); | |
| return vmsname; | |
| } | |
| while (*fptr == '/') | |
| fptr++; | |
| } | |
| { | |
| char *vp; | |
| char cwdbuf[VMSMAXPATHLEN+1]; | |
| vp = getcwd(cwdbuf, VMSMAXPATHLEN); | |
| if (vp == 0) | |
| { | |
| vmsname[0] = '\0'; | |
| return vmsname; /*FIXME, err getcwd */ | |
| } | |
| strcpy (vptr, vp); | |
| } | |
| if (*fptr == 0) | |
| { | |
| state = -1; | |
| break; | |
| } | |
| else | |
| { | |
| char *vp = strchr (vptr, ']'); | |
| if (vp == 0) | |
| { | |
| state = -1; | |
| break; | |
| } | |
| *vp = '\0'; | |
| nstate = N_OPEN; | |
| vptr += strlen (vptr); | |
| state = 9; | |
| } | |
| 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/ | |
| */ | |
| const char * | |
| unixify (const char *name) | |
| { | |
| static char piece[512]; | |
| const char *s; | |
| char *p; | |
| if (strchr (name, '/') != 0) /* already in unix style */ | |
| { | |
| strcpy (piece, name); | |
| return piece; | |
| } | |
| p = piece; | |
| *p = 0; | |
| /* device part */ | |
| s = strchr (name, ':'); | |
| if (s != 0) | |
| { | |
| int l = s - name; | |
| *p++ = '/'; | |
| *p++ = '/'; | |
| strncpy (p, name, l); | |
| p += l; | |
| } | |
| /* 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 */ |