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