628ac6d93037441d2dd7083e11cd1c95eacc6209
[platform/upstream/make.git] / vmsify.c
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, 2008, 2009 Free Software Foundation, Inc.
4 This file is part of GNU Make.
5
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.
10
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.
14
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/>.  */
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
65 static int
66 copyto (char **to, const char **from, char upto, int as_dir)
67 {
68   const 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
113 static char *
114 trnlog (const 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 = malloc (resltlen+1);
138   if (s == 0)
139     return "";
140   strcpy (s, reslt);
141   return s;
142 }
143
144 static char *
145 showall (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
165 enum 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
177 1       ./              <cwd>           []                      <current directory>.dir
178 2       ../             <home of cwd>   <home of cwd>           <home of cwd>.dir
179
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
183
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
191
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
198
199 22      a.b.c           a_b.c           [.a_b_c]                a_b_c.dir
200
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
203
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
207
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.
210 */
211
212 const char *
213 vmsify (const char *name, int type)
214 {
215 /* max 255 device
216    max 39 directory
217    max 39 filename
218    max 39 filetype
219    max 5 version
220 */
221 #define MAXPATHLEN 512
222
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;
230
231   if (name == 0)
232     return 0;
233   fptr = name;
234   vptr = vmsname;
235   nstate = N_START;
236
237   /* case 25a */
238   t = strpbrk (name, "$:");
239
240   if (t != 0)
241     {
242       const char *s1;
243       const char *s2;
244
245       if (type == 1)
246         {
247           s1 = strchr (t+1, '[');
248           s2 = strchr (t+1, ']');
249         }
250
251       if (*t == '$')
252         {
253           if (strchr (name, '/') == 0)
254             {
255               strcpy (vmsname, name);
256               if ((type == 1) && (s1 != 0) && (s2 == 0))
257                 strcat (vmsname, "]");
258               return vmsname;
259             }
260         }
261       else
262         {
263           strcpy (vmsname, name);
264           if ((type == 1) && (s1 != 0) && (s2 == 0))
265             strcat (vmsname, "]");
266           return vmsname;
267         }
268     }
269
270   /* case 26 */
271   t = strchr (name, '[');
272
273   if (t != 0)
274     {
275       const char *s;
276       const char *s1 = strchr (t+1, '[');
277       if (s1 == 0)
278         {
279           strcpy (vmsname, name);
280           if ((type == 1) && (strchr (t+1, ']') == 0))
281             strcat (vmsname, "]");
282           return vmsname;
283         }
284       s1--;
285       if (*s1 != ']')
286         {
287           strcpy (vmsname, name);
288           return vmsname;               /* not ][, keep unchanged */
289         }
290
291       /* we have ][ */
292
293       s = name;
294
295       /* s  -> starting char
296          s1 -> ending ']'  */
297       do
298         {
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);
312         }
313       while (1);
314
315       *vptr++ = ']';
316
317       fptr = s;
318
319     }
320   else          /* no [ in name */
321     {
322       int state = 0;
323       int rooted = 1;   /* flag if logical is rooted, else insert [000000] */
324
325       do
326         {
327       switch (state)
328         {
329           case 0:                               /* start of loop */
330             if (*fptr == '/')
331               {
332                 fptr++;
333                 state = 1;
334               }
335             else if (*fptr == '.')
336               {
337                 fptr++;
338                 state = 10;
339               }
340             else
341               state = 2;
342             break;
343
344           case 1:                               /* '/' at start */
345             if (*fptr == '/')
346               {
347                 fptr++;
348                 state = 3;
349               }
350             else
351               state = 4;
352             break;
353
354           case 2:                               /* no '/' at start */
355             {
356             const char *s = strchr (fptr, '/');
357             if (s == 0)                 /* no '/' (16) */
358               {
359                 if (type == 1)
360                   {
361                     strcpy (vptr, "[.");
362                     vptr += 2;
363                   }
364                 copyto (&vptr, &fptr, 0, (type==1));
365                 if (type == 1)
366                   *vptr++ = ']';
367                 state = -1;
368               }
369             else                        /* found '/' (17..21) */
370               {
371                 if ((type == 2)
372                     && (*(s+1) == 0))   /* 17(2) */
373                   {
374                     copyto (&vptr, &fptr, '/', 1);
375                     state = 7;
376                   }
377                 else
378                   {
379                     strcpy (vptr, "[.");
380                     vptr += 2;
381                     copyto (&vptr, &fptr, '/', 1);
382                     nstate = N_OPEN;
383                     state = 9;
384                   }
385               }
386             break;
387             }
388
389           case 3:                               /* '//' at start */
390             {
391             const char *s;
392             const char *s1;
393             char *vp;
394             while (*fptr == '/')        /* collapse all '/' */
395               fptr++;
396             if (*fptr == 0)             /* just // */
397               {
398                 char cwdbuf[MAXPATHLEN+1];
399
400                 s1 = getcwd(cwdbuf, MAXPATHLEN);
401                 if (s1 == 0)
402                   {
403                     vmsname[0] = '\0';
404                     return vmsname;     /* FIXME, err getcwd */
405                   }
406                 s = strchr (s1, ':');
407                 if (s == 0)
408                   {
409                     vmsname[0] = '\0';
410                     return vmsname;     /* FIXME, err no device */
411                   }
412                 strncpy (vptr, s1, s-s1+1);
413                 vptr += s-s1+1;
414                 state = -1;
415                 break;
416               }
417
418             s = vptr;
419
420             if (copyto (&vptr, &fptr, '/', 1) == 0)     /* copy device part */
421               {
422                 *vptr++ = ':';
423                 state = -1;
424                 break;
425               }
426             *vptr = ':';
427             nstate = N_DEVICE;
428             if (*fptr == 0)     /* just '//a/' */
429               {
430                 strcpy (vptr+1, "[000000]");
431                 vptr += 9;
432                 state = -1;
433                 break;
434               }
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 */
441                   {
442                     char *vp2 = vp + strlen (vp) - 1;
443                     if (*vp2 == ':')    /* translation ends in ':' */
444                       {
445                         vp2 = trnlog (vp);
446                         free (vp);
447                         if (*vp2 == 0)
448                           {
449                             rooted = 0;
450                             break;
451                           }
452                         vp = vp2;
453                         continue;       /* next iteration */
454                       }
455                     if (*vp2 == ']')    /* translation ends in ']' */
456                       {
457                         if (*(vp2-1) == '.')    /* ends in '.]' */
458                           {
459                             if (strncmp (fptr, "000000", 6) != 0)
460                               rooted = 0;
461                           }
462                         else
463                           {
464                             strcpy (vmsname, s1);
465                             vp = strchr (vmsname, ']');
466                             *vp = '.';
467                             nstate = N_DOT;
468                             vptr = vp;
469                           }
470                       }
471                     break;
472                   }
473                 free (vp);
474               }
475             else
476               rooted = 0;
477
478             if (*vptr == 0)
479               {
480                 nstate = N_DEVICE;
481                 *vptr++ = ':';
482               }
483             else
484               vptr++;
485
486             if (rooted == 0)
487               {
488                 nstate = N_DOT;
489                 strcpy (vptr, "[000000.");
490                 vptr += 8;
491                 vp = vptr-1;
492               }
493             else
494               vp = 0;
495
496             /* vp-> '.' after 000000 or NULL */
497
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;
508               }
509             else
510               {
511                 while (*(s+1) == '/')   /* skip multiple '/' */
512                   s++;
513               }
514
515             if ((rooted != 0)
516                 && (*(vptr-1) != '.'))
517               {
518                 *vptr++ = '[';
519                 nstate = N_DOT;
520               }
521             else
522               if ((nstate == N_DOT)
523                  && (vp != 0)
524                  && (*(s+1) == 0))
525                 {
526                   if (type == 2)
527                     {
528                       *vp = ']';
529                       nstate = N_CLOSED;
530                     }
531                 }
532             state = 9;
533             break;
534             }
535           case 4:                               /* single '/' at start (9..15) */
536             if (*fptr == 0)
537               state = 5;
538             else
539               state = 6;
540             break;
541
542           case 5:                               /* just '/' at start (9) */
543             if (type != 2)
544               {
545                 *vptr++ = '[';
546                 nstate = N_OPEN;
547               }
548             strcpy (vptr, "000000");
549             vptr += 6;
550             if (type == 2)
551               state = 7;
552             else
553               state = 8;
554             break;
555
556           case 6:               /* chars following '/' at start 10..15 */
557             {
558             const char *s;
559             *vptr++ = '[';
560             nstate = N_OPEN;
561             s = strchr (fptr, '/');
562             if (s == 0)                 /* 10 */
563               {
564                 if (type != 1)
565                   {
566                     strcpy (vptr, "000000]");
567                     vptr += 7;
568                   }
569                 copyto (&vptr, &fptr, 0, (type == 1));
570                 if (type == 1)
571                   {
572                     *vptr++ = ']';
573                   }
574                 state = -1;
575               }
576             else                        /* 11..15 */
577               {
578                 if ( (type == 2)
579                    && (*(s+1) == 0))    /* 11(2) */
580                   {
581                     strcpy (vptr, "000000]");
582                     nstate = N_CLOSED;
583                     vptr += 7;
584                   }
585                 copyto (&vptr, &fptr, '/', (*(vptr-1) != ']'));
586                 state = 9;
587               }
588             break;
589             }
590
591           case 7:                               /* add '.dir' and exit */
592             if ((nstate == N_OPEN)
593                 || (nstate == N_DOT))
594               {
595                 char *vp = vptr-1;
596                 while (vp > vmsname)
597                   {
598                     if (*vp == ']')
599                       {
600                         break;
601                       }
602                     if (*vp == '.')
603                       {
604                         *vp = ']';
605                         break;
606                       }
607                     vp--;
608                   }
609               }
610             strcpy (vptr, ".dir");
611             vptr += 4;
612             state = -1;
613             break;
614
615           case 8:                               /* add ']' and exit */
616             *vptr++ = ']';
617             state = -1;
618             break;
619
620           case 9:                       /* 17..21, fptr -> 1st '/' + 1 */
621             {
622             const char *s;
623             if (*fptr == 0)
624               {
625                 if (type == 2)
626                   {
627                     state = 7;
628                   }
629                 else
630                   state = 8;
631                 break;
632               }
633             s = strchr (fptr, '/');
634             if (s == 0)
635               {
636                 if (type != 1)
637                   {
638                     if (nstate == N_OPEN)
639                       {
640                         *vptr++ = ']';
641                         nstate = N_CLOSED;
642                       }
643                     as_dir = 0;
644                   }
645                 else
646                   {
647                     if (nstate == N_OPEN)
648                       {
649                         *vptr++ = '.';
650                         nstate = N_DOT;
651                       }
652                     as_dir = 1;
653                   }
654               }
655             else
656               {
657                 while (*(s+1) == '/')
658                   s++;
659                 if ( (type == 2)
660                     && (*(s+1) == 0))           /* 19(2), 21(2)*/
661                   {
662                     if (nstate != N_CLOSED)
663                       {
664                         *vptr++ = ']';
665                         nstate = N_CLOSED;
666                       }
667                     as_dir = 1;
668                   }
669                 else
670                   {
671                     if (nstate == N_OPEN)
672                       {
673                         *vptr++ = '.';
674                         nstate = N_DOT;
675                       }
676                     as_dir = 1;
677                   }
678               }
679             if ( (*fptr == '.')                 /* check for '..' or '../' */
680                 && (*(fptr+1) == '.')
681                 && ((*(fptr+2) == '/')
682                     || (*(fptr+2) == 0)) )
683               {
684                 char *vp;
685                 fptr += 2;
686                 if (*fptr == '/')
687                   {
688                     do
689                       {
690                         fptr++;
691                       }
692                     while (*fptr == '/');
693                   }
694                 else if (*fptr == 0)
695                   type = 1;
696                 vptr--;                         /* vptr -> '.' or ']' */
697                 vp = vptr;
698                 for (;;)
699                   {
700                     vp--;
701                     if (*vp == '.')             /* one back */
702                       {
703                         vptr = vp;
704                         nstate = N_OPEN;
705                         break;
706                       }
707                     if (*vp == '[')             /* top level reached */
708                       {
709                         if (*fptr == 0)
710                           {
711                             strcpy (vp, "[000000]");
712                             vptr = vp + 8;
713                             nstate = N_CLOSED;
714                             s = 0;
715                             break;
716                           }
717                         else
718                           {
719                             vptr = vp+1;
720                             nstate = N_OPEN;
721                             break;
722                           }
723                       }
724                   }
725               }
726             else
727               {
728                 copyto (&vptr, &fptr, '/', as_dir);
729                 if (nstate == N_DOT)
730                   nstate = N_OPEN;
731               }
732             if (s == 0)
733               {                                 /* 18,20 */
734                 if (type == 1)
735                   *vptr++ = ']';
736                 state = -1;
737               }
738             else
739               {
740                 if (*(s+1) == 0)
741                   {
742                     if (type == 2)              /* 19,21 */
743                       {
744                         state = 7;
745                       }
746                     else
747                       {
748                         *vptr++ = ']';
749                         state = -1;
750                       }
751                   }
752               }
753             break;
754             }
755
756           case 10:                              /* 1,2 first is '.' */
757             if (*fptr == '.')
758               {
759                 fptr++;
760                 state = 11;
761               }
762             else
763               state = 12;
764             break;
765
766           case 11:                              /* 2, '..' at start */
767             count = 1;
768             if (*fptr != 0)
769               {
770                 if (*fptr != '/')               /* got ..xxx */
771                   {
772                     strcpy (vmsname, name);
773                     return vmsname;
774                   }
775                 do                              /* got ../ */
776                   {
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++;
787                   }
788                 while (*fptr == '/');
789               }
790             {                                   /* got '..' or '../' */
791               char *vp;
792               char cwdbuf[MAXPATHLEN+1];
793
794               vp = getcwd(cwdbuf, MAXPATHLEN);
795               if (vp == 0)
796                 {
797                   vmsname[0] = '\0';
798                   return vmsname;    /* FIXME, err getcwd */
799                 }
800               strcpy (vptr, vp);
801               vp = strchr (vptr, ']');
802               if (vp != 0)
803                 {
804                   nstate = N_OPEN;
805                   while (vp > vptr)
806                     {
807                       vp--;
808                       if (*vp == '[')
809                         {
810                           vp++;
811                           strcpy (vp, "000000]");
812                           state = -1;
813                           break;
814                         }
815                       else if (*vp == '.')
816                         {
817                           if (--count == 0)
818                             {
819                               if (*fptr == 0)   /* had '..' or '../' */
820                                 {
821                                   *vp++ = ']';
822                                   state = -1;
823                                 }
824                               else                      /* had '../xxx' */
825                                 {
826                                   state = 9;
827                                 }
828                               *vp = '\0';
829                               break;
830                             }
831                         }
832                     }
833                 }
834               vptr += strlen (vptr);
835             }
836             break;
837
838           case 12:                              /* 1, '.' at start */
839             if (*fptr != 0)
840               {
841                 if (*fptr != '/')
842                   {
843                     strcpy (vmsname, name);
844                     return vmsname;
845                   }
846                 while (*fptr == '/')
847                   fptr++;
848               }
849
850             {
851               char *vp;
852               char cwdbuf[MAXPATHLEN+1];
853
854               vp = getcwd(cwdbuf, MAXPATHLEN);
855               if (vp == 0)
856                 {
857                   vmsname[0] = '\0';
858                   return vmsname;    /*FIXME, err getcwd */
859                 }
860               strcpy (vptr, vp);
861             }
862             if (*fptr == 0)
863               {
864                 state = -1;
865                 break;
866               }
867             else
868               {
869                 char *vp = strchr (vptr, ']');
870                 if (vp == 0)
871                   {
872                     state = -1;
873                     break;
874                   }
875                 *vp = '\0';
876                 nstate = N_OPEN;
877                 vptr += strlen (vptr);
878                 state = 9;
879               }
880             break;
881         }
882
883         }
884       while (state > 0);
885
886
887     }
888
889
890   /* directory conversion done
891      fptr -> filename part of input string
892      vptr -> free space in vmsname
893   */
894
895   *vptr++ = 0;
896
897   return vmsname;
898 }
899
900
901
902 /*
903   convert from vms-style to unix-style
904
905   dev:[dir1.dir2]       //dev/dir1/dir2/
906 */
907
908 const char *
909 unixify (const char *name)
910 {
911   static char piece[512];
912   const char *s;
913   char *p;
914
915   if (strchr (name, '/') != 0)          /* already in unix style */
916     {
917       strcpy (piece, name);
918       return piece;
919     }
920
921   p = piece;
922   *p = 0;
923
924   /* device part */
925
926   s = strchr (name, ':');
927
928   if (s != 0)
929     {
930       int l = s - name;
931       *p++ = '/';
932       *p++ = '/';
933       strncpy (p, name, l);
934       p += l;
935     }
936
937   /* directory part */
938
939   *p++ = '/';
940   s = strchr (name, '[');
941
942   if (s != 0)
943     {
944       s++;
945       switch (*s)
946         {
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;
959         }
960       s++;
961       while (*s)
962         {
963           if (*s == '.')
964             *p++ = '/';
965           else
966             *p++ = *s;
967           s++;
968           if (*s == ']')
969             {
970               s++;
971               break;
972             }
973         }
974       if (*s != 0)              /* more after ']' ?? */
975         {
976           if (*(p-1) != '/')
977             *p++ = '/';
978           strcpy (p, s);                /* copy it anyway */
979         }
980     }
981
982   else          /* no '[' anywhere */
983
984     {
985       *p++ = 0;
986     }
987
988   /* force end with '/' */
989
990   if (*(p-1) != '/')
991     *p++ = '/';
992   *p = 0;
993
994   return piece;
995 }
996
997 /* EOF */