xref: /trunk/main/tools/source/fsys/dirent.cxx (revision 89b56da7)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_tools.hxx"
26 
27 
28 #if !defined UNX
29 #include <io.h>
30 #include <process.h>
31 #endif
32 
33 #if defined(UNX) || defined(OS2)
34 #include <unistd.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #endif
38 
39 #include <ctype.h>
40 #include <errno.h>
41 #include <stdlib.h>
42 #include <stdio.h>
43 #include <string.h>
44 #include <tools/debug.hxx>
45 #include <tools/list.hxx>
46 #include "comdep.hxx"
47 #include <tools/fsys.hxx>
48 #define _TOOLS_HXX
49 #include <tools/urlobj.hxx>
50 
51 #ifdef UNX
52 #define _MAX_PATH 260
53 #endif
54 #include <tools/stream.hxx>
55 
56 #ifndef _VOS_MUTEX_HXX
57 #include <vos/mutex.hxx>
58 #endif
59 
60 #include <osl/file.hxx>
61 #include <rtl/instance.hxx>
62 
63 
64 using namespace osl;
65 using namespace rtl;
66 
67 int ApiRet2ToSolarError_Impl( int nApiRet );
68 
69 //--------------------------------------------------------------------
70 int Sys2SolarError_Impl( int nSysErr )
71 {
72     switch ( nSysErr )
73     {
74 #ifdef WNT
75                 case NO_ERROR:                                  return ERRCODE_NONE;
76                 case ERROR_INVALID_FUNCTION:    return ERRCODE_IO_GENERAL;
77                 case ERROR_FILE_NOT_FOUND:              return ERRCODE_IO_NOTEXISTS;
78                 case ERROR_PATH_NOT_FOUND:              return ERRCODE_IO_NOTEXISTSPATH;
79                 case ERROR_TOO_MANY_OPEN_FILES: return ERRCODE_IO_TOOMANYOPENFILES;
80                 case ERROR_ACCESS_DENIED:               return ERRCODE_IO_ACCESSDENIED;
81                 case ERROR_INVALID_HANDLE:              return ERRCODE_IO_GENERAL;
82                 case ERROR_NOT_ENOUGH_MEMORY:   return ERRCODE_IO_OUTOFMEMORY;
83                 case ERROR_INVALID_BLOCK:               return ERRCODE_IO_GENERAL;
84 //              case ERROR_BAD_ENVIRONMENT:     return ERRCODE_IO_;
85                 case ERROR_BAD_FORMAT:                  return ERRCODE_IO_WRONGFORMAT;
86                 case ERROR_INVALID_ACCESS:              return ERRCODE_IO_ACCESSDENIED;
87 //              case ERROR_INVALID_DATA:                return ERRCODE_IO_;
88                 case ERROR_INVALID_DRIVE:               return ERRCODE_IO_INVALIDDEVICE;
89                 case ERROR_CURRENT_DIRECTORY:   return ERRCODE_IO_CURRENTDIR;
90                 case ERROR_NOT_SAME_DEVICE:     return ERRCODE_IO_NOTSAMEDEVICE;
91 //              case ERROR_NO_MORE_FILES:               return ERRCODE_IO_;
92                 case ERROR_WRITE_PROTECT:               return ERRCODE_IO_CANTWRITE;
93                 case ERROR_BAD_UNIT:                    return ERRCODE_IO_INVALIDDEVICE;
94                 case ERROR_NOT_READY:                   return ERRCODE_IO_DEVICENOTREADY;
95                 case ERROR_BAD_COMMAND:                 return ERRCODE_IO_GENERAL;
96                 case ERROR_CRC:                                 return ERRCODE_IO_BADCRC;
97                 case ERROR_BAD_LENGTH:                  return ERRCODE_IO_INVALIDLENGTH;
98                 case ERROR_SEEK:                                return ERRCODE_IO_CANTSEEK;
99                 case ERROR_NOT_DOS_DISK:                return ERRCODE_IO_WRONGFORMAT;
100                 case ERROR_SECTOR_NOT_FOUND:    return ERRCODE_IO_GENERAL;
101                 case ERROR_WRITE_FAULT:                 return ERRCODE_IO_CANTWRITE;
102                 case ERROR_READ_FAULT:                  return ERRCODE_IO_CANTREAD;
103                 case ERROR_GEN_FAILURE:                 return ERRCODE_IO_GENERAL;
104                 case ERROR_SHARING_VIOLATION:   return ERRCODE_IO_LOCKVIOLATION;
105                 case ERROR_LOCK_VIOLATION:              return ERRCODE_IO_LOCKVIOLATION;
106                 case ERROR_WRONG_DISK:                  return ERRCODE_IO_INVALIDDEVICE;
107                 case ERROR_NOT_SUPPORTED:               return ERRCODE_IO_NOTSUPPORTED;
108 #else
109         case 0:         return ERRCODE_NONE;
110         case ENOENT:    return ERRCODE_IO_NOTEXISTS;
111         case EACCES:    return ERRCODE_IO_ACCESSDENIED;
112         case EEXIST:    return ERRCODE_IO_ALREADYEXISTS;
113         case EINVAL:    return ERRCODE_IO_INVALIDPARAMETER;
114         case EMFILE:    return ERRCODE_IO_TOOMANYOPENFILES;
115         case ENOMEM:    return ERRCODE_IO_OUTOFMEMORY;
116         case ENOSPC:    return ERRCODE_IO_OUTOFSPACE;
117 #endif
118     }
119 
120     DBG_TRACE1( "FSys: unknown system error %d occured", nSysErr );
121     return FSYS_ERR_UNKNOWN;
122 }
123 
124 //--------------------------------------------------------------------
125 
126 #ifndef BOOTSTRAP
127 
128 FSysRedirector* FSysRedirector::_pRedirector = 0;
129 sal_Bool FSysRedirector::_bEnabled = sal_True;
130 #ifdef UNX
131 sal_Bool bInRedirection = sal_True;
132 #else
133 sal_Bool bInRedirection = sal_False;
134 #endif
135 static vos:: OMutex * pRedirectMutex = 0;
136 
137 //------------------------------------------------------------------------
138 void FSysRedirector::Register( FSysRedirector *pRedirector )
139 {
140         if ( pRedirector )
141                 pRedirectMutex = new vos:: OMutex ;
142         else
143                 DELETEZ( pRedirectMutex );
144         _pRedirector = pRedirector;
145 }
146 
147 //------------------------------------------------------------------------
148 
149 void FSysRedirector::DoRedirect( String &rPath )
150 {
151         String aURL(rPath);
152 
153         // if redirection is disabled or not even registered do nothing
154         if ( !_bEnabled || !pRedirectMutex )
155                 return;
156 
157         // redirect only removable or remote volumes
158         if ( !IsRedirectable_Impl( ByteString( aURL, osl_getThreadTextEncoding() ) ) )
159                 return;
160 
161         // Redirection is acessible only by one thread per time
162         // dont move the guard behind the bInRedirection check!!!
163         // think of nested calls (when called from callback)
164         vos:: OGuard  aGuard( pRedirectMutex );
165 
166         // if already in redirection, dont redirect
167         if ( bInRedirection )
168                 return;
169 
170         // dont redirect on nested calls
171         bInRedirection = sal_True;
172 
173         // convert to URL
174 #ifndef UNX
175         for ( sal_Unicode *p = (sal_Unicode*)aURL.GetBuffer(); *p; ++p )
176                 if ( '\\' == *p ) *p = '/';
177                 else if ( ':' == *p ) *p = '|';
178 #endif
179 
180         aURL.Insert( String("file:///", osl_getThreadTextEncoding()), 0 );
181 
182         // do redirection
183         Redirector();
184 
185         bInRedirection = sal_False;
186         return;
187 }
188 
189 //------------------------------------------------------------------------
190 
191 FSysRedirector* FSysRedirector::Redirector()
192 {
193         if ( !_pRedirector )
194                 Register( new FSysRedirector );
195         return _pRedirector;
196 }
197 
198 #endif // BOOTSTRAP
199 
200 //--------------------------------------------------------------------
201 
202 class DirEntryStack: public List
203 {
204 public:
205                         DirEntryStack() {};
206                         ~DirEntryStack();
207 
208     inline  void        Push( DirEntry *pEntry );
209     inline  DirEntry*   Pop();
210     inline  DirEntry*   Top();
211     inline  DirEntry*   Bottom();
212 };
213 
214 inline void DirEntryStack::Push( DirEntry *pEntry )
215 {
216     List::Insert( pEntry, LIST_APPEND );
217 }
218 
219 inline DirEntry* DirEntryStack::Pop()
220 {
221     return (DirEntry*) List::Remove( Count() - 1 );
222 }
223 
224 inline DirEntry* DirEntryStack::Top()
225 {
226     return (DirEntry*) List::GetObject( Count() - 1 );
227 }
228 
229 inline DirEntry* DirEntryStack::Bottom()
230 {
231     return (DirEntry*) List::GetObject( 0 );
232 }
233 
234 //--------------------------------------------------------------------
235 
236 DBG_NAME( DirEntry );
237 
238 /*************************************************************************
239 |*
240 |*    DirEntry::~DirEntryStack()
241 |*
242 |*    Beschreibung      FSYS.SDW
243 |*    Ersterstellung    MI 26.04.91
244 |*    Letzte Aenderung  MI 04.07.91
245 |*
246 *************************************************************************/
247 
248 DirEntryStack::~DirEntryStack()
249 {
250     while ( Count() )
251         delete Pop();
252 }
253 
254 /*************************************************************************
255 |*
256 |*    ImpCheckDirEntry()
257 |*
258 |*    Beschreibung      Pruefung eines DirEntry fuer DBG_UTIL
259 |*    Parameter         void* p     Zeiger auf den DirEntry
260 |*    Return-Wert       char*       Fehlermeldungs-TExtension oder NULL
261 |*    Ersterstellung    MI 16.07.91
262 |*    Letzte Aenderung  MI 26.05.93
263 |*
264 *************************************************************************/
265 
266 #ifdef DBG_UTIL
267 const char* ImpCheckDirEntry( const void* p )
268 {
269     DirEntry* p0 = (DirEntry*)p;
270 
271     if ( p0->pParent )
272         DBG_CHKOBJ( p0->pParent, DirEntry, ImpCheckDirEntry );
273 
274     return NULL;
275 }
276 #endif
277 
278 /*************************************************************************
279 |*
280 |*    ImplCutPath()
281 |*
282 |*    Beschreibung      Fuegt ... ein, damit maximal nMaxChars lang
283 |*    Ersterstellung    MI 06.04.94
284 |*    Letzte Aenderung  DV 24.06.96
285 |*
286 *************************************************************************/
287 
288 ByteString ImplCutPath( const ByteString& rStr, sal_uInt16 nMax, char cAccDel )
289 {
290     sal_uInt16  nMaxPathLen = nMax;
291     ByteString  aCutPath( rStr );
292     sal_Bool    bInsertPrefix = sal_False;
293     sal_uInt16  nBegin = aCutPath.Search( cAccDel );
294 
295     if( nBegin == STRING_NOTFOUND )
296         nBegin = 0;
297     else
298         nMaxPathLen += 2;   // fuer Prefix <Laufwerk>:
299 
300     while( aCutPath.Len() > nMaxPathLen )
301     {
302         sal_uInt16 nEnd = aCutPath.Search( cAccDel, nBegin + 1 );
303         sal_uInt16 nCount;
304 
305         if ( nEnd != STRING_NOTFOUND )
306         {
307             nCount = nEnd - nBegin;
308             aCutPath.Erase( nBegin, nCount );
309             bInsertPrefix = sal_True;
310         }
311         else
312             break;
313     }
314 
315     if ( aCutPath.Len() > nMaxPathLen )
316     {
317         for ( sal_uInt16 n = nMaxPathLen; n > nMaxPathLen/2; --n )
318             if ( !ByteString(aCutPath.GetChar(n)).IsAlphaNumericAscii() )
319             {
320                 aCutPath.Erase( n );
321                 aCutPath += "...";
322                 break;
323             }
324     }
325 
326     if ( bInsertPrefix )
327     {
328         ByteString aIns( cAccDel );
329         aIns += "...";
330         aCutPath.Insert( aIns, nBegin );
331     }
332 
333     return aCutPath;
334 }
335 
336 /*************************************************************************
337 |*
338 |*    DirEntry::ImpParseOs2Name()
339 |*
340 |*    Beschreibung      FSYS.SDW
341 |*    Ersterstellung    MI 26.04.91
342 |*    Letzte Aenderung  MI 23.06.95
343 |*
344 *************************************************************************/
345 
346 FSysError DirEntry::ImpParseOs2Name( const ByteString& rPfad, FSysPathStyle eStyle  )
347 {
348     DBG_CHKTHIS( DirEntry, ImpCheckDirEntry );
349 
350     // die einzelnen Namen auf einen Stack packen
351     String       aPfad( rPfad, osl_getThreadTextEncoding() );
352     DirEntryStack   aStack;
353 
354     do
355     {
356         // den Namen vor dem ersten "\\" abspalten,
357         // falls '\\' am Anfang, ist der Name '\\',
358         // der Rest immer ohne die fuehrenden '\\'.
359         // ein ":" trennt ebenfalls, gehoert aber zum Namen
360         // den ersten '\\', '/' oder ':' suchen
361         sal_uInt16 nPos;
362         for ( nPos = 0;
363               nPos < aPfad.Len() &&                             //?O
364                   aPfad.GetChar(nPos) != '\\' && aPfad.GetChar(nPos) != '/' &&      //?O
365                   aPfad.GetChar(nPos) != ':';                               //?O
366               nPos++ )
367             /* do nothing */;
368 
369         // ist der Name ein UNC Pathname?
370         if ( nPos == 0 && aPfad.Len() > 1 &&
371              ( ( aPfad.GetChar(0) == '\\' && aPfad.GetChar(1) == '\\' ) ||
372                ( aPfad.GetChar(0) == '/' && aPfad.GetChar(1) == '/' ) ) )
373         {
374             for ( nPos = 2; aPfad.Len() > nPos; ++nPos )
375                 if ( aPfad.GetChar(nPos) == '\\' || aPfad.GetChar(nPos) == '/' )
376                     break;
377             aName = ByteString( aPfad.Copy( 2, nPos-2 ), osl_getThreadTextEncoding() );
378             aStack.Push( new DirEntry( aName, FSYS_FLAG_ABSROOT, eStyle ) );
379         }
380         // ist der Name die Root des aktuellen Drives?
381         else if ( nPos == 0 && aPfad.Len() > 0 &&
382                   ( aPfad.GetChar(0) == '\\' || aPfad.GetChar(0) == '/' ) )
383         {
384             // Root-Directory des aktuellen Drives
385             aStack.Push( new DirEntry( FSYS_FLAG_ABSROOT ) );
386         }
387         else
388         {
389             // ist der Name ein Drive?
390             if ( nPos < aPfad.Len() && aPfad.GetChar(nPos) == ':' )
391             {
392                 aName = ByteString( aPfad.Copy( 0, nPos + 1 ), osl_getThreadTextEncoding() );
393 
394                 // ist der Name die Root des Drives
395                 if ( (nPos + 1) < aPfad.Len() &&
396                      ( aPfad.GetChar(nPos+1) == '\\' || aPfad.GetChar(nPos+1) == '/' ) )
397                 {
398                     // schon was auf dem Stack?
399                     // oder Novell-Format? (not supported wegen URLs)
400                         if ( aStack.Count() || aName.Len() > 2 )
401                         {
402                             aName = rPfad;
403                             return FSYS_ERR_MISPLACEDCHAR;
404                         }
405                     // Root-Directory des Drive
406                     aStack.Push( new DirEntry( aName, FSYS_FLAG_ABSROOT, eStyle ) );
407                 }
408                 else
409                 {
410                     // liegt ein anderes Drive auf dem Stack?
411                     if ( aStack.Count() &&
412                          COMPARE_EQUAL != aStack.Bottom()->aName.CompareIgnoreCaseToAscii(aName) )
413                         aStack.Clear();
414 
415                     // liegt jetzt nichts mehr auf dem Stack?
416                     if ( !aStack.Count() )
417                         aStack.Push( new DirEntry( aName, FSYS_FLAG_RELROOT, eStyle ) );
418                 }
419             }
420 
421             // es ist kein Drive
422             else
423             {
424                 // den Namen ohne Trenner abspalten
425                 aName = ByteString( aPfad.Copy( 0, nPos ), osl_getThreadTextEncoding() );
426 
427                 // stellt der Name die aktuelle Directory dar?
428                 if ( aName == "." )
429                     /* do nothing */;
430 
431                 // stellt der Name die Parent-Directory dar?
432                 else if ( aName == ".." )
433                 {
434                     // ist nichts, ein Parent oder eine relative Root
435                     // auf dem Stack?
436                     if ( ( aStack.Count() == 0 ) ||
437                          ( aStack.Top()->eFlag == FSYS_FLAG_PARENT ) ||
438                          ( aStack.Top()->eFlag == FSYS_FLAG_RELROOT ) )
439                         // fuehrende Parents kommen auf den Stack
440                         aStack.Push( new DirEntry( FSYS_FLAG_PARENT ) );
441 
442                     // ist es eine absolute Root
443                     else if ( aStack.Top()->eFlag == FSYS_FLAG_ABSROOT )
444                     {
445                         // die hat keine Parent-Directory
446                         aName = rPfad;
447                         return FSYS_ERR_NOTEXISTS;
448                     }
449                     else
450                         // sonst hebt der Parent den TOS auf
451                         delete aStack.Pop();
452                 }
453 
454                 else
455                 {
456                     if ( eStyle == FSYS_STYLE_FAT )
457                     {
458                         // ist der Name grundsaetzlich ungueltig?
459                         int         nPunkte = 0;
460                         const char *pChar;
461                         for ( pChar = aName.GetBuffer();
462                               nPunkte < 2 && *pChar != 0;
463                               pChar++ )
464                         {
465                             if ( *pChar == ';' )
466                                 nPunkte = 0;
467                             else
468                                 nPunkte += ( *pChar == '.' ) ? 1 : 0;
469                         }
470                         if ( nPunkte > 1 )
471                         {
472                             aName = rPfad;
473                             return FSYS_ERR_MISPLACEDCHAR;
474                         }
475                     }
476 
477                     // normalen Entries kommen auf den Stack
478                                         DirEntry *pNew = new DirEntry( aName, FSYS_FLAG_NORMAL, eStyle );
479                                         if ( !pNew->IsValid() )
480                                         {
481                                                 aName = rPfad;
482                                                 ErrCode eErr = pNew->GetError();
483                                                 delete pNew;
484                                                 return eErr;
485                                         }
486                     aStack.Push( pNew );
487                 }
488             }
489         }
490 
491         // den Restpfad bestimmen
492         aPfad.Erase( 0, nPos + 1 );
493         while ( aPfad.Len() && ( aPfad.GetChar(0) == '\\' || aPfad.GetChar(0) == '/' ) )
494             aPfad.Erase( 0, 1 );
495     }
496     while ( aPfad.Len() );
497 
498     sal_uIntPtr nErr = ERRCODE_NONE;
499     // Haupt-Entry (selbst) zuweisen
500     if ( aStack.Count() == 0 )
501     {
502         eFlag = FSYS_FLAG_CURRENT;
503         aName.Erase();
504     }
505     else
506     {
507         eFlag = aStack.Top()->eFlag;
508         aName = aStack.Top()->aName;
509         nErr = aStack.Top()->nError;
510         delete aStack.Pop();
511     }
512 
513     // die Parent-Entries vom Stack holen
514     DirEntry** pTemp = &pParent; // Zeiger auf den Member pParent setzen
515     while ( aStack.Count() )
516     {
517         *pTemp = aStack.Pop();
518 
519         // Zeiger auf den Member pParent des eigenen Parent setzen
520         pTemp = &( (*pTemp)->pParent );
521     }
522 
523     // wird damit ein Volume beschrieben?
524     if ( !pParent && eFlag == FSYS_FLAG_RELROOT && aName.Len() )
525         eFlag = FSYS_FLAG_VOLUME;
526 
527     // bei gesetztem ErrorCode den Namen komplett "ubernehmen
528     if ( nErr )
529         aName = rPfad;
530     return nErr;
531 }
532 
533 /*************************************************************************
534 |*
535 |*    DirEntry::ImpParseName()
536 |*
537 |*    Beschreibung      FSYS.SDW
538 |*    Ersterstellung    MI 26.08.91
539 |*    Letzte Aenderung  MI 26.05.93
540 |*
541 *************************************************************************/
542 
543 FSysError DirEntry::ImpParseName( const ByteString& rbInitName,
544                                   FSysPathStyle eStyle )
545 {
546 	String	rInitName( rbInitName, osl_getThreadTextEncoding() );
547     if ( eStyle == FSYS_STYLE_HOST )
548         eStyle = DEFSTYLE;
549 
550     // KI-Division of FSys
551     if ( eStyle == FSYS_STYLE_DETECT )
552     {
553         sal_Unicode cFirst = rInitName.GetChar(0);
554         if ( rInitName.Len() == 2 && rInitName.GetChar(1) == ':' &&
555              ((cFirst >= 'A' && cFirst <= 'Z') ||
556               (cFirst >= 'a' && cFirst <= 'z')))
557            eStyle = FSYS_STYLE_HPFS;
558         else if ( rInitName.Len() > 2 && rInitName.GetChar(1) == ':' )
559         {
560             if ( rInitName.Search( ':', 2 ) == STRING_NOTFOUND )
561                 eStyle = FSYS_STYLE_HPFS;
562             else
563                 eStyle = FSYS_STYLE_MAC;
564         }
565         else if ( rInitName.Search( '/' ) != STRING_NOTFOUND )
566             eStyle = FSYS_STYLE_BSD;
567         else if ( rInitName.Search( '\\' ) != STRING_NOTFOUND )
568             eStyle = FSYS_STYLE_HPFS;
569         else if ( rInitName.Search( ':' ) != STRING_NOTFOUND )
570             eStyle = FSYS_STYLE_MAC;
571         else
572             eStyle = FSYS_STYLE_HPFS;
573     }
574 
575     switch ( eStyle )
576     {
577         case FSYS_STYLE_FAT:
578         case FSYS_STYLE_VFAT:
579         case FSYS_STYLE_HPFS:
580         case FSYS_STYLE_NTFS:
581         case FSYS_STYLE_NWFS:
582             return ImpParseOs2Name( rbInitName, eStyle );
583 
584         case FSYS_STYLE_BSD:
585         case FSYS_STYLE_SYSV:
586             return ImpParseUnixName( rbInitName, eStyle );
587 
588         case FSYS_STYLE_MAC:
589             return FSYS_ERR_OK;
590 
591         default:
592             return FSYS_ERR_UNKNOWN;
593     }
594 }
595 
596 /*************************************************************************
597 |*
598 |*    GetStyle()
599 |*
600 |*    Beschreibung      FSYS.SDW
601 |*    Ersterstellung    MI 15.11.91
602 |*    Letzte Aenderung  MI 15.11.91
603 |*
604 *************************************************************************/
605 
606 static FSysPathStyle GetStyle( FSysPathStyle eStyle )
607 {
608     if ( eStyle == FSYS_STYLE_HOST || eStyle == FSYS_STYLE_DETECT )
609         return DEFSTYLE;
610     else
611         return eStyle;
612 }
613 
614 /*************************************************************************
615 |*
616 |*    DirEntry::ImpTrim()
617 |*
618 |*    Beschreibung      bringt den Namen auf Betriebssystem-Norm
619 |*                      z.B. 8.3 lower beim MS-DOS Formatter
620 |*                      wirkt nicht rekursiv
621 |*    Ersterstellung    MI 12.08.91
622 |*    Letzte Aenderung  MI 21.05.92
623 |*
624 *************************************************************************/
625 
626 void DirEntry::ImpTrim( FSysPathStyle eStyle )
627 {
628     // Wildcards werden nicht geclipt
629     if ( ( aName.Search( '*' ) != STRING_NOTFOUND ) ||
630          ( aName.Search( '?' ) != STRING_NOTFOUND ) ||
631          ( aName.Search( ';' ) != STRING_NOTFOUND ) )
632         return;
633 
634     switch ( eStyle )
635     {
636         case FSYS_STYLE_FAT:
637         {
638             sal_uInt16 nPunktPos = aName.Search( '.' );
639             if ( nPunktPos == STRING_NOTFOUND )
640             {
641                 if ( aName.Len() > 8 )
642                 {
643                     nError = ERRCODE_IO_MISPLACEDCHAR|ERRCODE_WARNING_MASK;
644                     aName.Erase( 8 );
645                 }
646             }
647             else
648             {
649                 if ( nPunktPos > 8 )
650                 {
651                     nError = ERRCODE_IO_MISPLACEDCHAR|ERRCODE_WARNING_MASK;
652                     aName.Erase( 8, nPunktPos - 8 );
653                     nPunktPos = 8;
654                 }
655                 if ( aName.Len() > nPunktPos + 3 )
656                 {
657                     if ( aName.Len() - nPunktPos > 4 )
658                     {
659                         nError = ERRCODE_IO_MISPLACEDCHAR|ERRCODE_WARNING_MASK;
660                         aName.Erase( nPunktPos + 4 );
661                     }
662                 }
663             }
664             aName.ToLowerAscii();
665             break;
666         }
667 
668         case FSYS_STYLE_VFAT:
669         case FSYS_STYLE_HPFS:
670         case FSYS_STYLE_NTFS:
671         case FSYS_STYLE_NWFS:
672             if ( aName.Len() > 254 )
673             {
674                 nError = ERRCODE_IO_MISPLACEDCHAR|ERRCODE_WARNING_MASK;
675                 aName.Erase( 254 );
676             }
677 
678             if ( eStyle == FSYS_STYLE_HPFS &&
679                  ( eFlag == FSYS_FLAG_ABSROOT || eFlag == FSYS_FLAG_RELROOT ) )
680                 aName.ToUpperAscii();
681             break;
682 
683         case FSYS_STYLE_SYSV:
684             if ( aName.Len() > 14 )
685             {
686                 nError = ERRCODE_IO_MISPLACEDCHAR|ERRCODE_WARNING_MASK;
687                 aName.Erase( 14 );
688             }
689             break;
690 
691         case FSYS_STYLE_BSD:
692             if ( aName.Len() > 250 )
693             {
694                 nError = ERRCODE_IO_MISPLACEDCHAR|ERRCODE_WARNING_MASK;
695                 aName.Erase( 250 );
696             }
697             break;
698 
699         case FSYS_STYLE_MAC:
700             if ( eFlag & ( FSYS_FLAG_ABSROOT | FSYS_FLAG_VOLUME ) )
701             {
702                 if ( aName.Len() > 27 )
703                 {
704                     nError = ERRCODE_IO_MISPLACEDCHAR|ERRCODE_WARNING_MASK;
705                     aName.Erase( 27 );
706                 }
707             }
708             else
709             {
710                 if ( aName.Len() > 31 )
711                 {
712                     nError = ERRCODE_IO_MISPLACEDCHAR|ERRCODE_WARNING_MASK;
713                     aName.Erase( 31 );
714                 }
715             }
716             break;
717 
718         default:
719             /* kann nicht sein */;
720     }
721 }
722 
723 /*************************************************************************
724 |*
725 |*    DirEntry::DirEntry()
726 |*
727 |*    Beschreibung      FSYS.SDW
728 |*    Ersterstellung    MI 26.04.91
729 |*    Letzte Aenderung  MA 02.12.91
730 |*
731 *************************************************************************/
732 
733 DirEntry::DirEntry( const ByteString& rName, DirEntryFlag eDirFlag,
734                     FSysPathStyle eStyle ) :
735 #ifdef FEAT_FSYS_DOUBLESPEED
736             pStat( 0 ),
737 #endif
738             aName( rName )
739 {
740     DBG_CTOR( DirEntry, ImpCheckDirEntry );
741 
742     pParent         = NULL;
743     eFlag           = eDirFlag;
744     nError          = FSYS_ERR_OK;
745 
746     ImpTrim( eStyle );
747 }
748 
749 /*************************************************************************
750 |*
751 |*    DirEntry::DirEntry()
752 |*
753 |*    Beschreibung      FSYS.SDW
754 |*    Ersterstellung    MI 26.04.91
755 |*    Letzte Aenderung  MA 02.12.91
756 |*
757 *************************************************************************/
758 
759 DirEntry::DirEntry( const DirEntry& rOrig ) :
760 #ifdef FEAT_FSYS_DOUBLESPEED
761             pStat( rOrig.pStat ? new FileStat(*rOrig.pStat) : 0 ),
762 #endif
763             aName( rOrig.aName )
764 {
765     DBG_CTOR( DirEntry, ImpCheckDirEntry );
766 
767     eFlag           = rOrig.eFlag;
768     nError          = rOrig.nError;
769 
770     if ( rOrig.pParent )
771     {
772         pParent = new DirEntry( *rOrig.pParent );
773     }
774     else
775     {
776         pParent = NULL;
777     }
778 }
779 
780 /*************************************************************************
781 |*
782 |*    DirEntry::DirEntry()
783 |*
784 |*    Beschreibung      FSYS.SDW
785 |*    Ersterstellung    MI 26.04.91
786 |*    Letzte Aenderung  MA 02.12.91
787 |*
788 *************************************************************************/
789 
790 DirEntry::DirEntry( const String& rInitName, FSysPathStyle eStyle )
791 #ifdef FEAT_FSYS_DOUBLESPEED
792             : pStat( 0 )
793 #endif
794 {
795     DBG_CTOR( DirEntry, ImpCheckDirEntry );
796 
797     pParent         = NULL;
798 
799     // schnelle Loesung fuer Leerstring
800     if ( !rInitName.Len())
801     {
802         eFlag                   = FSYS_FLAG_CURRENT;
803         nError                  = FSYS_ERR_OK;
804         return;
805     }
806 
807     ByteString aTmpName(rInitName, osl_getThreadTextEncoding());
808     if( eStyle == FSYS_STYLE_URL || aTmpName.CompareIgnoreCaseToAscii("file:",5 ) == COMPARE_EQUAL )
809     {
810 #ifndef BOOTSTRAP
811         DBG_WARNING( "File URLs are not permitted but accepted" );
812         aTmpName = ByteString(String(INetURLObject( rInitName ).PathToFileName()), osl_getThreadTextEncoding());
813                 eStyle = FSYS_STYLE_HOST;
814 #endif // BOOTSTRAP
815     }
816     else
817     {
818         ::rtl::OUString aTmp;
819         ::rtl::OUString aOInitName;
820         if ( FileBase::getFileURLFromSystemPath( OUString( rInitName ), aTmp ) == FileBase::E_None )
821         {
822 			aOInitName = OUString( rInitName );
823             aTmpName = ByteString( String(aOInitName), osl_getThreadTextEncoding() );
824         }
825 
826 #ifdef DBG_UTIL
827         // ASF nur bei Default eStyle, nicht z.B. aus MakeShortName()
828         if( eStyle == FSYS_STYLE_HOST &&
829             aTmpName.Search( "://" ) != STRING_NOTFOUND )
830         {
831             ByteString aErr = "DirEntries akzeptieren nur File URLS: ";
832             aErr += aTmpName;
833             DBG_WARNING( aErr.GetBuffer() );
834         }
835 #endif
836     }
837 
838     nError  = ImpParseName( aTmpName, eStyle );
839 
840     if ( nError != FSYS_ERR_OK )
841         eFlag = FSYS_FLAG_INVALID;
842 }
843 
844 /*************************************************************************/
845 
846 DirEntry::DirEntry( const ByteString& rInitName, FSysPathStyle eStyle )
847 #ifdef FEAT_FSYS_DOUBLESPEED
848             : pStat( 0 )
849 #endif
850 {
851     DBG_CTOR( DirEntry, ImpCheckDirEntry );
852 
853     pParent         = NULL;
854 
855     // schnelle Loesung fuer Leerstring
856     if ( !rInitName.Len() )
857     {
858         eFlag                   = FSYS_FLAG_CURRENT;
859         nError                  = FSYS_ERR_OK;
860         return;
861     }
862 
863     ByteString aTmpName( rInitName );
864 	if( eStyle == FSYS_STYLE_URL || rInitName.CompareIgnoreCaseToAscii("file:",5 ) == COMPARE_EQUAL )
865     {
866 #ifndef BOOTSTRAP
867         DBG_WARNING( "File URLs are not permitted but accepted" );
868         aTmpName = ByteString(String(INetURLObject( rInitName ).PathToFileName()), osl_getThreadTextEncoding());
869 		eStyle = FSYS_STYLE_HOST;
870 #endif
871     }
872 #ifdef DBG_UTIL
873     else
874         // ASF nur bei Default eStyle, nicht z.B. aus MakeShortName()
875         if( eStyle == FSYS_STYLE_HOST &&
876             rInitName.Search( "://" ) != STRING_NOTFOUND )
877         {
878             ByteString aErr = "DirEntries akzeptieren nur File URLS: ";
879             aErr += rInitName;
880             DBG_WARNING( aErr.GetBuffer() );
881         }
882 #endif
883 
884     nError  = ImpParseName( aTmpName, eStyle );
885 
886     if ( nError != FSYS_ERR_OK )
887         eFlag = FSYS_FLAG_INVALID;
888 }
889 
890 /*************************************************************************
891 |*
892 |*    DirEntry::DirEntry()
893 |*
894 |*    Beschreibung      FSYS.SDW
895 |*    Ersterstellung    MI 26.04.91
896 |*    Letzte Aenderung  MA 02.12.91
897 |*
898 *************************************************************************/
899 
900 DirEntry::DirEntry( DirEntryFlag eDirFlag )
901 #ifdef FEAT_FSYS_DOUBLESPEED
902             : pStat( 0 )
903 #endif
904 {
905     DBG_CTOR( DirEntry, ImpCheckDirEntry );
906 
907     eFlag           = eDirFlag;
908     nError          = ( eFlag == FSYS_FLAG_INVALID ) ? FSYS_ERR_UNKNOWN : FSYS_ERR_OK;
909     pParent         = NULL;
910 }
911 
912 /*************************************************************************
913 |*
914 |*    DirEntry::~DirEntry()
915 |*
916 |*    Beschreibung      FSYS.SDW
917 |*    Ersterstellung    MI 26.04.91
918 |*    Letzte Aenderung  MA 02.12.91
919 |*
920 *************************************************************************/
921 
922 DirEntry::~DirEntry()
923 {
924     DBG_DTOR( DirEntry, ImpCheckDirEntry );
925 
926     delete pParent;
927 #ifdef FEAT_FSYS_DOUBLESPEED
928     delete pStat;
929 #endif
930 
931 }
932 
933 /*************************************************************************
934 |*
935 |*    DirEntry::ImpGetTopPtr() const
936 |*
937 |*    Beschreibung      FSYS.SDW
938 |*    Ersterstellung    MI 26.04.91
939 |*    Letzte Aenderung  MA 02.12.91
940 |*
941 *************************************************************************/
942 
943 const DirEntry* DirEntry::ImpGetTopPtr() const
944 {
945     DBG_CHKTHIS( DirEntry, ImpCheckDirEntry );
946 
947     const DirEntry *pTemp = this;
948     while ( pTemp->pParent )
949         pTemp = pTemp->pParent;
950 
951     return pTemp;
952 }
953 
954 /*************************************************************************
955 |*
956 |*    DirEntry::ImpGetTopPtr()
957 |*
958 |*    Beschreibung      FSYS.SDW
959 |*    Ersterstellung    MI 13.11.91
960 |*    Letzte Aenderung  MA 02.12.91
961 |*
962 *************************************************************************/
963 
964 DirEntry* DirEntry::ImpGetTopPtr()
965 {
966     DBG_CHKTHIS( DirEntry, ImpCheckDirEntry );
967 
968     DirEntry *pTemp = this;
969     while ( pTemp->pParent )
970         pTemp = pTemp->pParent;
971 
972     return pTemp;
973 }
974 
975 /*************************************************************************
976 |*
977 |*    DirEntry::ImpGetPreTopPtr()
978 |*
979 |*    Beschreibung      liefert einen Pointer auf den vorletzten Entry
980 |*    Ersterstellung    MI 01.11.91
981 |*    Letzte Aenderung  MA 02.12.91
982 |*
983 *************************************************************************/
984 
985 DirEntry* DirEntry::ImpGetPreTopPtr()
986 {
987     DBG_CHKTHIS( DirEntry, ImpCheckDirEntry );
988 
989     DirEntry *pTemp = this;
990     if ( pTemp->pParent )
991     {
992         while ( pTemp->pParent->pParent )
993             pTemp = pTemp->pParent;
994     }
995 
996     return pTemp;
997 }
998 
999 /*************************************************************************
1000 |*
1001 |*    DirEntry::ImpChangeParent()
1002 |*
1003 |*    Beschreibung      FSYS.SDW
1004 |*    Ersterstellung    MI 26.04.91
1005 |*    Letzte Aenderung  MI 21.05.92
1006 |*
1007 *************************************************************************/
1008 
1009 DirEntry* DirEntry::ImpChangeParent( DirEntry* pNewParent, sal_Bool bNormalize )
1010 {
1011     DBG_CHKTHIS( DirEntry, ImpCheckDirEntry );
1012 
1013     DirEntry *pTemp = pParent;
1014     if ( bNormalize && pNewParent &&
1015          pNewParent->eFlag == FSYS_FLAG_RELROOT && !pNewParent->aName.Len() )
1016     {
1017         pParent = 0;
1018         delete pNewParent;
1019     }
1020     else
1021         pParent = pNewParent;
1022 
1023     return pTemp;
1024 }
1025 
1026 /*************************************************************************
1027 |*
1028 |*    DirEntry::Exists()
1029 |*
1030 |*    Beschreibung      FSYS.SDW
1031 |*    Ersterstellung    MI 26.04.91
1032 |*    Letzte Aenderung  MI 24.09.91
1033 |*
1034 *************************************************************************/
1035 
1036 sal_Bool DirEntry::Exists( FSysAccess nAccess ) const
1037 {
1038 #ifndef BOOTSTRAP
1039 	static vos::OMutex aLocalMutex;
1040 	vos::OGuard aGuard( aLocalMutex );
1041 #endif
1042         if ( !IsValid() )
1043                 return sal_False;
1044 
1045 #if defined WNT || defined OS2
1046     // spezielle Filenamen sind vom System da
1047     if ( ( aName.CompareIgnoreCaseToAscii("CLOCK$") == COMPARE_EQUAL ||
1048            aName.CompareIgnoreCaseToAscii("CON") == COMPARE_EQUAL ||
1049            aName.CompareIgnoreCaseToAscii("AUX") == COMPARE_EQUAL ||
1050            aName.CompareIgnoreCaseToAscii("COM1") == COMPARE_EQUAL ||
1051            aName.CompareIgnoreCaseToAscii("COM2") == COMPARE_EQUAL ||
1052            aName.CompareIgnoreCaseToAscii("COM3") == COMPARE_EQUAL ||
1053            aName.CompareIgnoreCaseToAscii("COM4") == COMPARE_EQUAL ||
1054            aName.CompareIgnoreCaseToAscii("LPT1") == COMPARE_EQUAL ||
1055            aName.CompareIgnoreCaseToAscii("LPT2") == COMPARE_EQUAL ||
1056            aName.CompareIgnoreCaseToAscii("LPT3") == COMPARE_EQUAL ||
1057            aName.CompareIgnoreCaseToAscii("NUL") == COMPARE_EQUAL ||
1058            aName.CompareIgnoreCaseToAscii("PRN") == COMPARE_EQUAL ) )
1059         return sal_True;
1060 #endif
1061 
1062         FSysFailOnErrorImpl();
1063         DirEntryKind eKind = FileStat( *this, nAccess ).GetKind();
1064         if ( eKind & ( FSYS_KIND_FILE | FSYS_KIND_DIR ) )
1065         {
1066                 return sal_True;
1067         }
1068 
1069 #if defined WNT || defined OS2
1070         if ( 0 != ( eKind & FSYS_KIND_DEV ) )
1071         {
1072                 return DRIVE_EXISTS( ImpGetTopPtr()->aName.GetChar(0) );
1073         }
1074 #endif
1075 
1076         return 0 != ( eKind & ( FSYS_KIND_FILE | FSYS_KIND_DIR ) );
1077 }
1078 
1079 /*************************************************************************
1080 |*
1081 |*    DirEntry::First()
1082 |*
1083 |*    Beschreibung      FSYS.SDW
1084 |*    Ersterstellung    MI 26.04.91
1085 |*    Letzte Aenderung  MA 15.01.92
1086 |*
1087 *************************************************************************/
1088 
1089 sal_Bool DirEntry::First()
1090 {
1091     FSysFailOnErrorImpl();
1092 
1093         String    aUniPathName( GetPath().GetFull() );
1094 #ifndef BOOTSTRAP
1095         FSysRedirector::DoRedirect( aUniPathName );
1096 		ByteString aPathName(aUniPathName, osl_getThreadTextEncoding());
1097 #else
1098 		ByteString aPathName(aUniPathName, gsl_getSystemTextEncoding());
1099 #endif
1100         aPathName = GUI2FSYS( aPathName );
1101 
1102         DIR      *pDir = opendir( (char*) aPathName.GetBuffer() );
1103         if ( pDir )
1104         {
1105 #ifndef BOOTSTRAP
1106                 WildCard aWildeKarte( String(CMP_LOWER( aName ), osl_getThreadTextEncoding()) );
1107 #else
1108                 WildCard aWildeKarte( String(CMP_LOWER( aName ), gsl_getSystemTextEncoding()) );
1109 #endif
1110                 for ( dirent* pEntry = readdir( pDir );
1111                           pEntry;
1112                           pEntry = readdir( pDir ) )
1113                 {
1114                         ByteString aFound( FSYS2GUI( ByteString( pEntry->d_name ) ) );
1115                         if ( aWildeKarte.Matches( String(CMP_LOWER( aFound ), osl_getThreadTextEncoding())))
1116                         {
1117                                 aName = aFound;
1118                                 closedir( pDir );
1119                                 return sal_True;
1120                         }
1121                 }
1122                 closedir( pDir );
1123         }
1124         return sal_False;
1125 }
1126 
1127 /*************************************************************************
1128 |*
1129 |*    DirEntry::GetFull()
1130 |*
1131 |*    Beschreibung      FSYS.SDW
1132 |*    Ersterstellung    MI 26.04.91
1133 |*    Letzte Aenderung  MA 02.12.91
1134 |*
1135 *************************************************************************/
1136 
1137 String DirEntry::GetFull( FSysPathStyle eStyle, sal_Bool bWithDelimiter,
1138                           sal_uInt16 nMaxChars ) const
1139 {
1140     DBG_CHKTHIS( DirEntry, ImpCheckDirEntry );
1141 
1142     ByteString aRet;
1143     eStyle = GetStyle( eStyle );
1144     if ( pParent )
1145     {
1146         if ( ( pParent->eFlag == FSYS_FLAG_ABSROOT ||
1147                pParent->eFlag == FSYS_FLAG_RELROOT ||
1148                pParent->eFlag == FSYS_FLAG_VOLUME ) )
1149         {
1150             aRet  = ByteString(pParent->GetName( eStyle ), osl_getThreadTextEncoding());
1151             aRet += ByteString(GetName( eStyle ), osl_getThreadTextEncoding());
1152         }
1153         else
1154         {
1155             aRet  = ByteString(pParent->GetFull( eStyle ), osl_getThreadTextEncoding());
1156             aRet += ACCESSDELIM_C(eStyle);
1157             aRet += ByteString(GetName( eStyle ), osl_getThreadTextEncoding());
1158         }
1159     }
1160     else
1161     {
1162         aRet = ByteString(GetName( eStyle ), osl_getThreadTextEncoding());
1163     }
1164 
1165     if ( ( eStyle == FSYS_STYLE_MAC ) &&
1166          ( ImpGetTopPtr()->eFlag != FSYS_FLAG_VOLUME )  &&
1167          ( ImpGetTopPtr()->eFlag != FSYS_FLAG_ABSROOT ) &&
1168          ( aRet.GetChar(0) != ':' ) )
1169         aRet.Insert( ACCESSDELIM_C(eStyle), 0 );
1170 
1171     //! Hack
1172     if ( bWithDelimiter )
1173         if ( aRet.GetChar( aRet.Len()-1 ) != ACCESSDELIM_C(eStyle) )
1174             aRet += ACCESSDELIM_C(eStyle);
1175 
1176     //! noch ein Hack
1177     if ( nMaxChars < STRING_MAXLEN )
1178         aRet = ImplCutPath( aRet, nMaxChars, ACCESSDELIM_C(eStyle) );
1179 
1180     return String(aRet, osl_getThreadTextEncoding());
1181 }
1182 
1183 /*************************************************************************
1184 |*
1185 |*    DirEntry::GetPath()
1186 |*
1187 |*    Beschreibung      FSYS.SDW
1188 |*    Ersterstellung    MI 26.04.91
1189 |*    Letzte Aenderung  MA 02.12.91
1190 |*
1191 *************************************************************************/
1192 
1193 DirEntry DirEntry::GetPath() const
1194 {
1195     DBG_CHKTHIS( DirEntry, ImpCheckDirEntry );
1196 
1197     if ( pParent )
1198         return DirEntry( *pParent );
1199 
1200     return DirEntry();
1201 }
1202 
1203 /*************************************************************************
1204 |*
1205 |*    DirEntry::GetExtension()
1206 |*
1207 |*    Beschreibung      FSYS.SDW
1208 |*    Ersterstellung    MI 26.04.91
1209 |*    Letzte Aenderung  MA 02.12.91
1210 |*
1211 *************************************************************************/
1212 
1213 String DirEntry::GetExtension( char cSep ) const
1214 {
1215     DBG_CHKTHIS( DirEntry, ImpCheckDirEntry );
1216 
1217     const char *p0 = ( aName.GetBuffer() );
1218     const char *p1 = p0 + aName.Len() - 1;
1219     while ( p1 >= p0 && *p1 != cSep )
1220     p1--;
1221 
1222     if ( p1 >= p0 )
1223         // es wurde ein cSep an der Position p1 gefunden
1224         return String(
1225             aName.Copy( static_cast< xub_StrLen >(p1 - p0 + 1) ),
1226             osl_getThreadTextEncoding());
1227     return String();
1228 }
1229 
1230 /*************************************************************************
1231 |*
1232 |*    DirEntry::GetBase()
1233 |*
1234 |*    Beschreibung      FSYS.SDW
1235 |*    Ersterstellung    MI 26.04.91
1236 |*    Letzte Aenderung  MA 02.12.91
1237 |*
1238 *************************************************************************/
1239 
1240 String DirEntry::GetBase( char cSep ) const
1241 {
1242     DBG_CHKTHIS( DirEntry, ImpCheckDirEntry );
1243 
1244     const char *p0 = ( aName.GetBuffer() );
1245     const char *p1 = p0 + aName.Len() - 1;
1246     while ( p1 >= p0 && *p1 != cSep )
1247         p1--;
1248 
1249     if ( p1 >= p0 )
1250         // es wurde ein cSep an der Position p1 gefunden
1251         return String(
1252             aName.Copy( 0, static_cast< xub_StrLen >(p1 - p0) ),
1253             osl_getThreadTextEncoding());
1254 
1255     else
1256         // es wurde kein cSep gefunden
1257         return String(aName, osl_getThreadTextEncoding());
1258 }
1259 
1260 /*************************************************************************
1261 |*
1262 |*    DirEntry::GetName()
1263 |*
1264 |*    Beschreibung      FSYS.SDW
1265 |*    Ersterstellung    MI 26.04.91
1266 |*    Letzte Aenderung  MA 02.12.91 13:47
1267 |*
1268 *************************************************************************/
1269 
1270 String DirEntry::GetName( FSysPathStyle eStyle ) const
1271 {
1272     DBG_CHKTHIS( DirEntry, ImpCheckDirEntry );
1273 
1274     ByteString aRet;
1275     eStyle = GetStyle( eStyle );
1276 
1277     switch( eFlag )
1278     {
1279         case FSYS_FLAG_PARENT:
1280             aRet = ACTPARENT(eStyle);
1281                         break;
1282 
1283         case FSYS_FLAG_ABSROOT:
1284         {
1285             if ( eStyle == FSYS_STYLE_URL )
1286             {
1287                 aRet = "file:///";
1288                 aRet += aName;
1289 
1290 #ifndef UNX
1291                 if ( aName.Len())
1292                 {
1293                     if ( aName.GetChar(aName.Len()-1) == ':' )
1294                     {
1295                         aRet.SetChar(aRet.Len()-1, '|');
1296                     }
1297                     else
1298                     {
1299                         aRet.Insert( '/', 5 );
1300                     }
1301                     aRet += "/";
1302                 }
1303 #endif
1304             }
1305             else if ( eStyle != FSYS_STYLE_MAC &&
1306                                  aName.Len() > 1 && aName.GetChar( 1 ) != ':'  )
1307             {
1308                 // UNC-Pathname
1309                 aRet = ACCESSDELIM_C(eStyle);
1310                 aRet += ACCESSDELIM_C(eStyle);
1311                 aRet += aName ;
1312                 aRet += ACCESSDELIM_C(eStyle);
1313             }
1314             else
1315             {
1316                 aRet = aName;
1317                 aRet += ACCESSDELIM_C(eStyle);
1318             }
1319             break;
1320         }
1321 
1322         case FSYS_FLAG_INVALID:
1323         case FSYS_FLAG_VOLUME:
1324         {
1325             if ( eStyle == FSYS_STYLE_URL )
1326             {
1327                 aRet = "file:///";
1328                 aRet += aName;
1329 #ifndef UNX
1330 				if ( aName.Len() && aName.GetChar(aName.Len()-1) == ':' )
1331 				{
1332 					aRet.SetChar(aRet.Len()-1, '|');
1333 				}
1334 #endif
1335             }
1336             else
1337 			{
1338                 aRet = aName;
1339 			}
1340 
1341             break;
1342         }
1343 
1344         case FSYS_FLAG_RELROOT:
1345             if ( !aName.Len() )
1346             {
1347                 aRet = ACTCURRENT(eStyle);
1348                 break;
1349             }
1350 
1351         default:
1352             aRet = aName;
1353     }
1354 
1355     return String(aRet, osl_getThreadTextEncoding());
1356 }
1357 
1358 /*************************************************************************
1359 |*
1360 |*    DirEntry::IsAbs()
1361 |*
1362 |*    Beschreibung      FSYS.SDW
1363 |*    Ersterstellung    MI 26.04.91
1364 |*    Letzte Aenderung  MA 02.12.91
1365 |*
1366 *************************************************************************/
1367 
1368 bool DirEntry::IsAbs() const
1369 {
1370     DBG_CHKTHIS( DirEntry, ImpCheckDirEntry );
1371 
1372 #ifdef UNX
1373     return ( pParent ? pParent->IsAbs() : eFlag == FSYS_FLAG_ABSROOT );
1374 #else
1375     return ( pParent ? pParent->IsAbs() : eFlag == FSYS_FLAG_ABSROOT && aName.Len() > 0 );
1376 #endif
1377 }
1378 
1379 /*************************************************************************
1380 |*
1381 |*    DirEntry::CutName()
1382 |*
1383 |*    Beschreibung      FSYS.SDW
1384 |*    Ersterstellung    MI 26.04.91
1385 |*    Letzte Aenderung  MA 02.12.91
1386 |*
1387 *************************************************************************/
1388 
1389 String DirEntry::CutName( FSysPathStyle eStyle )
1390 {
1391     DBG_CHKTHIS( DirEntry, ImpCheckDirEntry );
1392 
1393     eStyle = GetStyle( eStyle );
1394 
1395     String aOldName( GetName( eStyle ) );
1396 
1397     if ( pParent )
1398     {
1399         DirEntry *pOldParent = pParent;
1400         if ( pOldParent )
1401         {
1402             pParent = pOldParent->pParent;
1403             eFlag = pOldParent->eFlag;
1404             aName = pOldParent->aName;
1405             pOldParent->pParent = NULL;
1406             delete pOldParent;
1407         }
1408         else
1409         {
1410             eFlag = FSYS_FLAG_CURRENT;
1411             aName.Erase();
1412         }
1413     }
1414     else
1415     {
1416         eFlag = FSYS_FLAG_CURRENT;
1417         aName.Erase();
1418         delete pParent;
1419         pParent = NULL;
1420     }
1421 
1422     return aOldName;
1423 }
1424 
1425 /*************************************************************************
1426 |*
1427 |*    DirEntry::NameCompare
1428 |*
1429 |*    Beschreibung      Vergleich nur die Namen (ohne Pfad, aber mit Gross/Klein)
1430 |*    Ersterstellung    MI 26.04.91
1431 |*    Letzte Aenderung  MA 02.12.91
1432 |*
1433 *************************************************************************/
1434 
1435 StringCompare DirEntry::NameCompare( const DirEntry &rWith ) const
1436 {
1437         ByteString aThisName;
1438         ByteString aParameterName;
1439 
1440 #ifdef UNX
1441                 aThisName = aName;
1442                 aParameterName = rWith.aName;
1443 #else
1444                 aThisName = ByteString(aName).ToLowerAscii();
1445                 aParameterName = ByteString(rWith.aName).ToLowerAscii();
1446 #endif
1447 
1448     return aThisName.CompareTo( aParameterName );
1449 }
1450 
1451 
1452 /*************************************************************************
1453 |*
1454 |*    DirEntry::operator==()
1455 |*
1456 |*    Beschreibung      FSYS.SDW
1457 |*    Ersterstellung    MI 26.04.91
1458 |*    Letzte Aenderung  MA 02.12.91
1459 |*
1460 *************************************************************************/
1461 
1462 sal_Bool DirEntry::operator==( const DirEntry& rEntry ) const
1463 {
1464     DBG_CHKTHIS( DirEntry, ImpCheckDirEntry );
1465 
1466     // test wheather the contents are textual the same
1467 
1468     if ( nError && ( nError == rEntry.nError ) )
1469         return sal_True;
1470     if ( nError || rEntry.nError ||
1471          ( eFlag == FSYS_FLAG_INVALID ) ||
1472          ( rEntry.eFlag == FSYS_FLAG_INVALID ) )
1473         return sal_False;
1474 
1475 #ifndef OS2
1476     const
1477 #endif
1478 	DirEntry *pThis = (DirEntry *)this;
1479 #ifndef OS2
1480     const
1481 #endif
1482 	DirEntry *pWith = (DirEntry *)&rEntry;
1483     while( pThis && pWith && (pThis->eFlag == pWith->eFlag) )
1484     {
1485         if ( CMP_LOWER(pThis->aName) != CMP_LOWER(pWith->aName) )
1486             break;
1487         pThis = pThis->pParent;
1488         pWith = pWith->pParent;
1489     }
1490 
1491     return ( !pThis && !pWith );
1492 }
1493 
1494 /*************************************************************************
1495 |*
1496 |*    DirEntry::operator=()
1497 |*
1498 |*    Beschreibung      FSYS.SDW
1499 |*    Ersterstellung    MI 26.04.91
1500 |*    Letzte Aenderung  MA 02.12.91
1501 |*
1502 *************************************************************************/
1503 
1504 DirEntry& DirEntry::operator=( const DirEntry& rEntry )
1505 {
1506     DBG_CHKTHIS( DirEntry, ImpCheckDirEntry );
1507 
1508     if ( this == &rEntry )
1509         return *this;
1510     if ( rEntry.nError != FSYS_ERR_OK ) {
1511 		DBG_ERROR("Zuweisung mit invalidem DirEntry");
1512         nError = rEntry.nError;
1513         return *this;
1514     }
1515 
1516     // Name und Typ uebernehmen, Refs beibehalten
1517     aName                       = rEntry.aName;
1518     eFlag                       = rEntry.eFlag;
1519     nError                      = FSYS_ERR_OK;
1520 
1521     DirEntry *pOldParent = pParent;
1522     if ( rEntry.pParent )
1523         pParent = new DirEntry( *rEntry.pParent );
1524     else
1525         pParent = NULL;
1526 
1527     if ( pOldParent )
1528         delete pOldParent;
1529     return *this;
1530 }
1531 
1532 /*************************************************************************
1533 |*
1534 |*    DirEntry::operator+()
1535 |*
1536 |*    Beschreibung      FSYS.SDW
1537 |*    Ersterstellung    MI 26.04.91
1538 |*    Letzte Aenderung  MA 02.12.91
1539 |*
1540 *************************************************************************/
1541 
1542 DirEntry DirEntry::operator+( const DirEntry& rEntry ) const
1543 {
1544     DBG_CHKTHIS( DirEntry, ImpCheckDirEntry );
1545 #ifdef DBG_UTIL
1546         static sal_Bool bTested = sal_False;
1547         if ( !bTested )
1548         {
1549                 bTested = sal_True;
1550                 FSysTest();
1551         }
1552 #endif
1553 
1554         const DirEntry *pEntryTop = rEntry.ImpGetTopPtr();
1555         const DirEntry *pThisTop = ImpGetTopPtr();
1556 
1557         // "." + irgendwas oder irgendwas + "d:irgendwas"
1558 /* TPF:org
1559     if ( ( eFlag == FSYS_FLAG_RELROOT && !aName ) ||
1560                  ( pEntryTop->aName.Len() &&
1561                         ( pEntryTop->eFlag == FSYS_FLAG_ABSROOT ||
1562                       pEntryTop->eFlag == FSYS_FLAG_RELROOT ||
1563                           pEntryTop->eFlag == FSYS_FLAG_VOLUME ) ) )
1564                 return rEntry;
1565 */
1566 
1567     if (
1568         (eFlag == FSYS_FLAG_RELROOT && !aName.Len()) ||
1569         (
1570          (pEntryTop->aName.Len()  ||
1571           ((rEntry.Level()>1)?(rEntry[rEntry.Level()-2].aName.CompareIgnoreCaseToAscii(RFS_IDENTIFIER)==COMPARE_EQUAL):sal_False))
1572           &&
1573          (pEntryTop->eFlag == FSYS_FLAG_ABSROOT ||
1574           pEntryTop->eFlag == FSYS_FLAG_RELROOT ||
1575           pEntryTop->eFlag == FSYS_FLAG_VOLUME)
1576         )
1577        )
1578     {
1579                 return rEntry;
1580     }
1581 
1582     // irgendwas + "." (=> pEntryTop == &rEntry)
1583     if ( pEntryTop->eFlag == FSYS_FLAG_RELROOT && !pEntryTop->aName.Len() )
1584     {
1585                 DBG_ASSERT( pEntryTop == &rEntry, "DirEntry::op+ buggy" );
1586                 return *this;
1587     }
1588 
1589     // root += ".." (=> unmoeglich)
1590         if ( pEntryTop->eFlag == FSYS_FLAG_PARENT && pThisTop == this &&
1591                 ( eFlag == FSYS_FLAG_ABSROOT ) )
1592                 return DirEntry( FSYS_FLAG_INVALID );
1593 
1594         // irgendwas += abs (=> nur Device uebernehmen falls vorhanden)
1595         if ( pEntryTop->eFlag == FSYS_FLAG_ABSROOT )
1596         {
1597                 ByteString aDevice;
1598                 if ( pThisTop->eFlag == FSYS_FLAG_ABSROOT )
1599                         aDevice = pThisTop->aName;
1600                 DirEntry aRet = rEntry;
1601                 if ( aDevice.Len() )
1602                         aRet.ImpGetTopPtr()->aName = aDevice;
1603                 return aRet;
1604         }
1605 
1606         // irgendwas += ".." (=> aufloesen)
1607         if ( eFlag == FSYS_FLAG_NORMAL && pEntryTop->eFlag == FSYS_FLAG_PARENT )
1608         {
1609                 String aConcated( GetFull() );
1610                 aConcated += ACCESSDELIM_C(FSYS_STYLE_HOST);
1611                 aConcated += rEntry.GetFull();
1612                 return DirEntry( aConcated );
1613         }
1614 
1615         // sonst einfach hintereinander haengen
1616         DirEntry aRet( rEntry );
1617         DirEntry *pTop = aRet.ImpGetTopPtr();
1618         pTop->pParent = new DirEntry( *this );
1619 
1620         return aRet;
1621 }
1622 
1623 /*************************************************************************
1624 |*
1625 |*    DirEntry::operator+=()
1626 |*
1627 |*    Beschreibung      FSYS.SDW
1628 |*    Ersterstellung    MI 26.04.91
1629 |*    Letzte Aenderung  MA 02.12.91
1630 |*
1631 *************************************************************************/
1632 
1633 DirEntry &DirEntry::operator+=( const DirEntry& rEntry )
1634 {
1635     DBG_CHKTHIS( DirEntry, ImpCheckDirEntry );
1636 
1637     return *this = *this + rEntry;
1638 }
1639 
1640 /*************************************************************************
1641 |*
1642 |*    DirEntry::GetAccessDelimiter()
1643 |*
1644 |*    Beschreibung      FSYS.SDW
1645 |*    Ersterstellung    MI 27.05.93
1646 |*    Letzte Aenderung  MI 10.06.93
1647 |*
1648 *************************************************************************/
1649 
1650 String DirEntry::GetAccessDelimiter( FSysPathStyle eFormatter )
1651 {
1652         return String( ACCESSDELIM_C( GetStyle( eFormatter ) ) );
1653 }
1654 
1655 /*************************************************************************
1656 |*
1657 |*    DirEntry::SetExtension()
1658 |*
1659 |*    Beschreibung      FSYS.SDW
1660 |*    Ersterstellung    MI 02.08.91
1661 |*    Letzte Aenderung  MA 02.12.91
1662 |*
1663 *************************************************************************/
1664 
1665 void DirEntry::SetExtension( const String& rExtension, char cSep )
1666 {
1667     DBG_CHKTHIS( DirEntry, ImpCheckDirEntry );
1668 
1669     // do not set extensions for drives
1670     if(eFlag == FSYS_FLAG_ABSROOT)
1671     {
1672         nError = FSYS_ERR_NOTSUPPORTED;
1673         return;
1674     }
1675 
1676     // cSep im Namen suchen
1677     const char *p0 = ( aName.GetBuffer() );
1678     const char *p1 = p0 + aName.Len() - 1;
1679     while ( p1 >= p0 && *p1 != cSep )
1680         p1--;
1681     if ( p1 >= p0 )
1682     {
1683         // es wurde ein cSep an der Position p1 gefunden
1684         aName.Erase(
1685             static_cast< xub_StrLen >(
1686                 p1 - p0 + 1 - ( rExtension.Len() ? 0 : 1 )) );
1687         aName += ByteString(rExtension, osl_getThreadTextEncoding());
1688     }
1689     else if ( rExtension.Len() )
1690     {
1691         // es wurde kein cSep gefunden
1692         aName += cSep;
1693         aName += ByteString(rExtension, osl_getThreadTextEncoding());
1694     }
1695 }
1696 
1697 /*************************************************************************
1698 |*
1699 |*    DirEntry::CutExtension()
1700 |*
1701 |*    Beschreibung      FSYS.SDW
1702 |*    Ersterstellung    MI 23.07.93
1703 |*    Letzte Aenderung  MI 23.07.93
1704 |*
1705 *************************************************************************/
1706 
1707 String DirEntry::CutExtension( char cSep )
1708 {
1709     DBG_CHKTHIS( DirEntry, ImpCheckDirEntry );
1710 
1711     const char *p0 = ( aName.GetBuffer() );
1712     const char *p1 = p0 + aName.Len() - 1;
1713     while ( p1 >= p0 && *p1 != cSep )
1714         p1--;
1715 
1716     if ( p1 >= p0 )
1717     {
1718         // es wurde ein cSep an der Position p1 gefunden
1719         aName.Erase( static_cast< xub_StrLen >(p1-p0) );
1720         return String(p1 + 1, osl_getThreadTextEncoding());
1721     }
1722 
1723     return String();
1724 }
1725 
1726 /*************************************************************************
1727 |*
1728 |*    DirEntry::SetName()
1729 |*
1730 |*    Beschreibung      FSYS.SDW
1731 |*    Ersterstellung    MI 04.09.93
1732 |*    Letzte Aenderung  MI 04.09.93
1733 |*
1734 *************************************************************************/
1735 
1736 void DirEntry::SetName( const String& rName, FSysPathStyle eFormatter )
1737 {
1738     DBG_CHKTHIS( DirEntry, ImpCheckDirEntry );
1739 
1740         if ( eFormatter == FSYS_STYLE_HOST || eFormatter == FSYS_STYLE_DETECT )
1741         eFormatter = DEFSTYLE;
1742     ByteString aAccDelim( ACCESSDELIM_C( eFormatter ) );
1743 
1744     if ( (eFlag != FSYS_FLAG_NORMAL) ||
1745          (aName.Search( ':' ) != STRING_NOTFOUND) ||
1746          (aName.Search( aAccDelim ) != STRING_NOTFOUND) ||
1747          (eFormatter == FSYS_STYLE_FAT && (aName.GetTokenCount( '.' ) > 2) ) )
1748     {
1749         eFlag = FSYS_FLAG_INVALID;
1750     }
1751     else
1752         {
1753         aName = ByteString(rName, osl_getThreadTextEncoding());
1754         }
1755 }
1756 
1757 /*************************************************************************
1758 |*
1759 |*    DirEntry::Find()
1760 |*
1761 |*    Beschreibung      FSYS.SDW
1762 |*    Ersterstellung    MI 26.04.91
1763 |*    Letzte Aenderung  MA 02.12.91
1764 |*
1765 *************************************************************************/
1766 sal_Bool DirEntry::Find( const String& rPfad, char cDelim )
1767 {
1768     DBG_CHKTHIS( DirEntry, ImpCheckDirEntry );
1769 
1770         if ( ImpGetTopPtr()->eFlag == FSYS_FLAG_ABSROOT )
1771                 return sal_True;
1772 
1773         sal_Bool bWild = aName.Search( '*' ) != STRING_NOTFOUND ||
1774                                  aName.Search( '?' ) != STRING_NOTFOUND;
1775         if ( !cDelim )
1776                 cDelim = SEARCHDELIM(DEFSTYLE)[0];
1777 
1778         sal_uInt16 nTokenCount = rPfad.GetTokenCount( cDelim );
1779         sal_uInt16 nIndex = 0;
1780         ByteString aThis = ACCESSDELIM(DEFSTYLE);
1781         aThis += ByteString(GetFull(), osl_getThreadTextEncoding());
1782         for ( sal_uInt16 nToken = 0; nToken < nTokenCount; ++nToken )
1783         {
1784             ByteString aPath = ByteString(rPfad, osl_getThreadTextEncoding()).GetToken( 0, cDelim, nIndex );
1785 
1786 			if ( aPath.Len() )
1787 			{
1788                 if (aPath.GetChar(aPath.Len()-1)== ACCESSDELIM(DEFSTYLE)[0])
1789                         aPath.Erase(aPath.Len()-1);
1790                 aPath += aThis;
1791                 DirEntry aEntry( String(aPath, osl_getThreadTextEncoding()));
1792                 if ( aEntry.ToAbs() &&
1793                          ( ( !bWild && aEntry.Exists() ) || ( bWild && aEntry.First() ) ) )
1794                 {
1795                         (*this) = aEntry;
1796                         return sal_True;
1797                 }
1798 			}
1799         }
1800         return sal_False;
1801 }
1802 
1803 /*************************************************************************
1804 |*
1805 |*    DirEntry::ImpToRel()
1806 |*
1807 |*    Beschreibung
1808 |*    Ersterstellung    MI 17.06.93
1809 |*    Letzte Aenderung  MI 17.06.93
1810 |*
1811 *************************************************************************/
1812 
1813 sal_Bool DirEntry::ImpToRel( String aCurStr )
1814 {
1815     DBG_CHKTHIS( DirEntry, ImpCheckDirEntry );
1816 
1817         DirEntry aThis(*this);
1818     aThis.ToAbs();
1819     String aThisStr( aThis.GetFull( FSYS_STYLE_HPFS ) );
1820 
1821     // #109512 preserve case of path even if caseinsensitive
1822     String aThisCompareStr( aThisStr ), aCurCompareStr( aCurStr );
1823     if ( ! IsCaseSensitive() )
1824     {
1825         aThisCompareStr.ToLowerAscii();
1826         aCurCompareStr.ToLowerAscii();
1827     }
1828 
1829     // "Ubereinstimmung pr"ufen
1830     sal_uInt16 nPos = aThisCompareStr.Match( aCurCompareStr );
1831     if ( nPos == STRING_MATCH && aThisStr.Len() != aCurStr.Len() )
1832         nPos = Min( aThisStr.Len(), aCurStr.Len() );
1833 
1834     // Sonderfall, die DirEntries sind identisch
1835     if ( nPos == STRING_MATCH )
1836     {
1837         // dann ist der relative Pfad das aktuelle Verzeichnis
1838         *this = DirEntry();
1839         return sal_True;
1840     }
1841 
1842     // Sonderfall, die DirEntries sind total verschieden
1843     if ( nPos == 0 )
1844     {
1845         // dann ist der relativste Pfad absolut
1846         *this = aThis;
1847         return sal_False;
1848     }
1849 
1850     // sonst nehmen wir die identischen Einzelteile vorne weg
1851     while ( nPos > 0 && aThisStr.GetChar(nPos) != '\\' )
1852         --nPos;
1853         aThisStr.Erase( 0, nPos + ( ( aThisStr.GetChar(nPos) == '\\' ) ? 1 : 0 ) );
1854     aCurStr.Erase( 0, nPos + ( ( aCurStr.GetChar(nPos) == '\\' ) ? 1 : 0 ) );
1855 
1856     // und fuellen mit dem Level der Directories auf
1857     for ( nPos = 0; nPos < aCurStr.Len(); ++nPos )
1858         if ( aCurStr.GetChar(nPos) == '\\' )
1859             aThisStr.Insert( String( "..\\", osl_getThreadTextEncoding() ), 0 );
1860 
1861     // das ist dann unser relativer Pfad
1862     *this = DirEntry( aThisStr, FSYS_STYLE_HPFS );
1863     return sal_True;
1864 }
1865 
1866 /*************************************************************************
1867 |*
1868 |*    DirEntry::CutRelParents()
1869 |*
1870 |*    Beschreibung
1871 |*    Ersterstellung    MI 01.08.95
1872 |*    Letzte Aenderung  MI 01.08.95
1873 |*
1874 *************************************************************************/
1875 
1876 sal_uInt16 DirEntry::CutRelParents()
1877 {
1878     DBG_CHKTHIS( DirEntry, ImpCheckDirEntry );
1879 
1880         // erstes '..' finden
1881     DirEntry *pDir = 0;
1882     DirEntry *pPar;
1883 
1884     for (  pPar = this;
1885           pPar && pPar->eFlag != FSYS_FLAG_PARENT;
1886           pPar = pPar->pParent )
1887         pDir = pPar;
1888 
1889     // '..' zaehlen
1890     sal_uInt16 nParCount = 0;
1891     while ( pPar && pPar->eFlag == FSYS_FLAG_PARENT )
1892     {
1893         ++nParCount;
1894         pPar = pPar->pParent;
1895     }
1896 
1897     // cutten
1898     if ( pDir )
1899         DELETEZ(pDir->pParent);
1900     else
1901         eFlag = FSYS_FLAG_CURRENT;
1902 
1903     return nParCount;
1904 }
1905 
1906 /*************************************************************************
1907 |*
1908 |*    DirEntry::ToRel()
1909 |*
1910 |*    Beschreibung      FSYS.SDW
1911 |*    Ersterstellung    MI 26.06.93
1912 |*    Letzte Aenderung  MI 17.06.93
1913 |*
1914 *************************************************************************/
1915 
1916 sal_Bool DirEntry::ToRel()
1917 {
1918     DBG_CHKTHIS( DirEntry, ImpCheckDirEntry );
1919 
1920         DirEntry aCur;
1921     aCur.ToAbs();
1922         return ImpToRel( aCur.GetFull( FSYS_STYLE_HPFS ) );
1923 }
1924 
1925 /*************************************************************************
1926 |*
1927 |*    DirEntry::ToRel()
1928 |*
1929 |*    Beschreibung      FSYS.SDW
1930 |*    Ersterstellung    MI 26.04.91
1931 |*    Letzte Aenderung  MA 02.12.91
1932 |*
1933 *************************************************************************/
1934 
1935 sal_Bool DirEntry::ToRel( const DirEntry& rStart )
1936 {
1937     DBG_CHKTHIS( DirEntry, ImpCheckDirEntry );
1938 
1939         DirEntry aStart( rStart );
1940         aStart.ToAbs();
1941         return ImpToRel( aStart.GetFull( FSYS_STYLE_HPFS ) );
1942 }
1943 
1944 /*************************************************************************
1945 |*
1946 |*    DirEntry::GetDevice()
1947 |*
1948 |*    Beschreibung      FSYS.SDW
1949 |*    Ersterstellung    MI 26.04.91
1950 |*    Letzte Aenderung  MA 02.12.91
1951 |*
1952 *************************************************************************/
1953 
1954 #ifndef UNX
1955 
1956 DirEntry DirEntry::GetDevice() const
1957 {
1958         DBG_CHKTHIS( DirEntry, ImpCheckDirEntry );
1959 
1960         const DirEntry *pTop = ImpGetTopPtr();
1961 
1962         if ( ( pTop->eFlag == FSYS_FLAG_ABSROOT || pTop->eFlag == FSYS_FLAG_RELROOT ) &&
1963                  pTop->aName.Len() )
1964                 return DirEntry( pTop->aName, FSYS_FLAG_VOLUME, FSYS_STYLE_HOST );
1965         else
1966                 return DirEntry( ByteString(), FSYS_FLAG_INVALID, FSYS_STYLE_HOST );
1967 }
1968 
1969 #endif
1970 
1971 /*************************************************************************
1972 |*
1973 |*    DirEntry::SetBase()
1974 |*
1975 |*    Beschreibung      FSYS.SDW
1976 |*    Ersterstellung    MI 23.10.91
1977 |*    Letzte Aenderung  MA 02.12.91
1978 |*
1979 *************************************************************************/
1980 
1981 void DirEntry::SetBase( const String& rBase, char cSep )
1982 {
1983     DBG_CHKTHIS( DirEntry, ImpCheckDirEntry );
1984 
1985     const char *p0 = ( aName.GetBuffer() );
1986     const char *p1 = p0 + aName.Len() - 1;
1987     while ( p1 >= p0 && *p1 != cSep )
1988         p1--;
1989 
1990     if ( p1 >= p0 )
1991     {
1992         // es wurde ein cSep an der Position p1 gefunden
1993         aName.Erase( 0, static_cast< xub_StrLen >(p1 - p0) );
1994         aName.Insert( ByteString(rBase, osl_getThreadTextEncoding()), 0 );
1995     }
1996     else
1997         aName = ByteString(rBase, osl_getThreadTextEncoding());
1998 }
1999 
2000 /*************************************************************************
2001 |*
2002 |*    DirEntry::GetSearchDelimiter()
2003 |*
2004 |*    Beschreibung      FSYS.SDW
2005 |*    Ersterstellung    MI 10.06.93
2006 |*    Letzte Aenderung  MI 10.06.93
2007 |*
2008 *************************************************************************/
2009 
2010 String DirEntry::GetSearchDelimiter( FSysPathStyle eFormatter )
2011 {
2012     return String( ByteString(SEARCHDELIM( GetStyle( eFormatter ) ) ), osl_getThreadTextEncoding());
2013 }
2014 
2015 /*************************************************************************
2016 |*
2017 |*    DirEntry::GetMaxNameLen()
2018 |*
2019 |*    Beschreibung      Liefert die maximale Anzahl von Zeichen in
2020 |*                      einzelnen Namensteile. Bei FileSystmen mit
2021 |*                      fester Extension (FAT) zaehlt diese nicht mit.
2022 |*                      Bei unbekannten FileSytemen und FSYS_STYLE_URL
2023 |*                      wird USHRT_MAX zurueckgegeben.
2024 |*    Ersterstellung    MI 17.06.97
2025 |*    Letzte Aenderung  MI 17.06.97
2026 |*
2027 *************************************************************************/
2028 
2029 sal_uInt16 DirEntry::GetMaxNameLen( FSysPathStyle eFormatter )
2030 {
2031     eFormatter = GetStyle( eFormatter );
2032     switch ( eFormatter )
2033     {
2034         case FSYS_STYLE_MAC:    return  31;
2035 
2036         case FSYS_STYLE_FAT:    return   8;
2037 
2038         case FSYS_STYLE_VFAT:
2039         case FSYS_STYLE_NTFS:
2040         case FSYS_STYLE_NWFS:
2041         case FSYS_STYLE_HPFS:   return 255;
2042 
2043 
2044         case FSYS_STYLE_SYSV:   return  14;
2045 
2046         case FSYS_STYLE_BSD:    return 250;
2047 
2048         default:
2049             return USHRT_MAX;
2050     }
2051 }
2052 
2053 /*************************************************************************
2054 |*
2055 |*    DirEntry::TempName()
2056 |*
2057 |*    Beschreibung      FSYS.SDW - Aha, wo?
2058 |*    Ersterstellung    VB 06.09.93 (im SWG)
2059 |*    Letzte Aenderung  MI 06.02.98
2060 |*
2061 *************************************************************************/
2062 namespace { struct TempNameBase_Impl : public rtl::Static< DirEntry, TempNameBase_Impl > {}; }
2063 
2064 const DirEntry& DirEntry::SetTempNameBase( const String &rBase )
2065 {
2066         DirEntry aTempDir = DirEntry().TempName().GetPath();
2067         aTempDir += DirEntry( rBase );
2068 #ifdef UNX
2069         ByteString aName( aTempDir.GetFull(), osl_getThreadTextEncoding());
2070         if ( access( aName.GetBuffer(), W_OK | X_OK | R_OK ) )
2071         {
2072 			// Create the directory and only on success give all rights to
2073 			// everyone. Use mkdir instead of DirEntry::MakeDir because
2074 			// this returns sal_True even if directory already exists.
2075 
2076 			if ( !mkdir( aName.GetBuffer(), S_IRWXU | S_IRWXG | S_IRWXO ) )
2077 				chmod( aName.GetBuffer(), S_IRWXU | S_IRWXG | S_IRWXO );
2078 
2079 			// This will not create a directory but perhaps FileStat called
2080 			// there modifies the DirEntry
2081 
2082 			aTempDir.MakeDir();
2083         }
2084 #else
2085         aTempDir.MakeDir();
2086 #endif
2087         DirEntry &rEntry = TempNameBase_Impl::get();
2088         rEntry = aTempDir.TempName( FSYS_KIND_DIR );
2089         return rEntry;
2090 }
2091 
2092 DirEntry DirEntry::TempName( DirEntryKind eKind ) const
2093 {
2094         // ggf. Base-Temp-Dir verwenden (macht Remote keinen Sinn => vorher)
2095 	const DirEntry &rEntry = TempNameBase_Impl::get();
2096         if ( !pParent && FSYS_FLAG_CURRENT != rEntry.eFlag && FSYS_FLAG_ABSROOT != eFlag )
2097 
2098         {
2099                 DirEntry aFactory( rEntry );
2100                 aFactory += GetName();
2101                 return aFactory.TempName();
2102         }
2103 
2104         ByteString aDirName; // hiermit hatte MPW C++ Probleme - immmer noch??
2105         char *ret_val;
2106         size_t i;
2107 
2108         // dertermine Directory, Prefix and Extension
2109         char pfx[6];
2110         char ext[5];
2111         const char *dir;
2112         const char *pWild = strchr( aName.GetBuffer(), '*' );
2113         if ( !pWild )
2114             pWild = strchr( aName.GetBuffer(), '?' );
2115 
2116         if ( pWild )
2117         {
2118             if ( pParent )
2119                 aDirName = ByteString(pParent->GetFull(), osl_getThreadTextEncoding());
2120             strncpy( pfx, aName.GetBuffer(), Min( (int)5, (int)(pWild-aName.GetBuffer()) ) );
2121             pfx[ pWild-aName.GetBuffer() ] = 0;
2122             const char *pExt = strchr( pWild, '.' );
2123             if ( pExt )
2124             {
2125                 strncpy( ext, pExt, 4 );
2126                 ext[4] = 0;
2127             }
2128             else
2129                 strcpy( ext, ".tmp" );
2130         }
2131         else
2132         {
2133             aDirName = ByteString(GetFull(), osl_getThreadTextEncoding());
2134             strcpy( pfx, "sv" );
2135             strcpy( ext, ".tmp" );
2136         }
2137         dir = aDirName.GetBuffer();
2138 
2139         // wurde kein Dir angegeben, dann nehmen wir ein passendes TEMP-Verz.
2140         char sBuf[_MAX_PATH];
2141         if ( eFlag == FSYS_FLAG_CURRENT || ( !pParent && pWild ) )
2142             dir = TempDirImpl(sBuf);
2143 
2144         // ab hier leicht modifizierter Code von VB
2145         DirEntry aRet(FSYS_FLAG_INVALID);
2146         i = strlen(dir);
2147         // need to add ?\\? + prefix + number + pid + .ext + '\0'
2148 #       define TMPNAME_SIZE  ( 1 + 5 + 5 + 10 + 4 + 1 )
2149         ret_val = new char[i + TMPNAME_SIZE ];
2150         if (ret_val)
2151         {
2152             strcpy(ret_val,dir);
2153 
2154             /* Make sure directory ends with a separator    */
2155 #if defined(WNT) || defined(OS2)
2156             if ( i>0 && ret_val[i-1] != '\\' && ret_val[i-1] != '/' &&
2157                  ret_val[i-1] != ':')
2158                 ret_val[i++] = '\\';
2159 #elif defined UNX
2160             if (i>0 && ret_val[i-1] != '/')
2161                 ret_val[i++] = '/';
2162 #else
2163 #error unknown operating system
2164 #endif
2165 
2166             strncpy(ret_val + i, pfx, 5);
2167             ret_val[i + 5] = '\0';      /* strncpy doesn't put a 0 if more  */
2168             i = strlen(ret_val);        /* than 'n' chars.          */
2169 
2170             /* Prefix can have 5 chars, leaving 3 for numbers. 26 ** 3 == 17576
2171              * Welcome to the 21st century, we can have longer filenames now ;)
2172              * New format: pfx + "5 char milli/micro second res" + "current pid" + ".tmp"
2173              */
2174 #if (defined MSC || defined __MINGW32__) && defined WNT
2175             /* Milliseconds !! */
2176             static unsigned long u = GetTickCount();
2177             unsigned long mypid = static_cast<unsigned long>(_getpid());
2178 #else
2179             /* Microseconds !! */
2180             static unsigned long u = clock();
2181             unsigned long mypid = static_cast<unsigned long>(getpid());
2182 #endif
2183             for ( unsigned long nOld = u; ++u != nOld; ) /* Hae??? */
2184             {
2185                 u %= 100000;  /* on *NIX repeats every 100ms, maybe less if CLOCKS_PER_SEC > 10^6 */
2186                 snprintf(ret_val+i, TMPNAME_SIZE, "%05lu%lu", u, mypid);
2187 
2188                 strcat(ret_val,ext);
2189 
2190                         if ( FSYS_KIND_FILE == eKind )
2191                         {
2192                                 SvFileStream aStream( String( ret_val, osl_getThreadTextEncoding()),
2193                                                         STREAM_WRITE|STREAM_SHARE_DENYALL );
2194                                 if ( aStream.IsOpen() )
2195                                 {
2196                                         aStream.Seek( STREAM_SEEK_TO_END );
2197                                         if ( 0 == aStream.Tell() )
2198                                         {
2199                                                 aRet = DirEntry( String( ret_val, osl_getThreadTextEncoding()));
2200                                                 break;
2201                                         }
2202                                         aStream.Close();
2203                                 }
2204                         }
2205                         else
2206                         {
2207                                 // Redirect
2208 				String aRetVal(ret_val, osl_getThreadTextEncoding());
2209                                 String aRedirected (aRetVal);
2210 #ifndef BOOTSTRAP
2211                                 FSysRedirector::DoRedirect( aRedirected );
2212 #endif
2213                                 if ( FSYS_KIND_DIR == eKind )
2214                                 {
2215                                                 if ( 0 == _mkdir( ByteString(aRedirected.GetBuffer(), osl_getThreadTextEncoding()).GetBuffer() ) )
2216                                         {
2217                                                 aRet = DirEntry( aRetVal );
2218                                                 break;
2219                                         }
2220                                 }
2221                                 else
2222                                 {
2223 #if defined(UNX) || defined(OS2)
2224 										if( access( ByteString(aRedirected, osl_getThreadTextEncoding()).GetBuffer(), F_OK ) )
2225                                         {
2226                                                 aRet = DirEntry( aRetVal );
2227                                                 break;
2228                                         }
2229 #else
2230                                         struct stat aStat;
2231                                         if ( stat( ByteString(aRedirected, osl_getThreadTextEncoding()).GetBuffer(), &aStat ) )
2232                                         {
2233 											aRet = DirEntry( aRetVal );
2234 											break;
2235                                         }
2236 #endif
2237                                 }
2238                         }
2239             }
2240 
2241             delete[] ret_val;
2242             ret_val = 0;
2243         }
2244 
2245         return aRet;
2246 }
2247 
2248 /*************************************************************************
2249 |*
2250 |*    DirEntry::operator[]()
2251 |*
2252 |*    Beschreibung      FSYS.SDW
2253 |*    Ersterstellung    MI 03.03.92
2254 |*    Letzte Aenderung  MI 03.03.92
2255 |*
2256 *************************************************************************/
2257 
2258 const DirEntry &DirEntry::operator[]( sal_uInt16 nParentLevel ) const
2259 {
2260     DBG_CHKTHIS( DirEntry, ImpCheckDirEntry );
2261 
2262         //TPF: maybe to be implemented (FastFSys)
2263 
2264         const DirEntry *pRes = this;
2265     while ( pRes && nParentLevel-- )
2266         pRes = pRes->pParent;
2267 
2268     return *pRes;
2269 }
2270 
2271 /*************************************************************************
2272 |*
2273 |*    DirEntry::ImpParseUnixName()
2274 |*
2275 |*    Beschreibung      FSYS.SDW
2276 |*    Ersterstellung    MI 26.04.91
2277 |*    Letzte Aenderung  MI 26.05.93
2278 |*
2279 *************************************************************************/
2280 
2281 FSysError DirEntry::ImpParseUnixName( const ByteString& rPfad, FSysPathStyle eStyle )
2282 {
2283     DBG_CHKTHIS( DirEntry, ImpCheckDirEntry );
2284 
2285     // die einzelnen Namen auf einen Stack packen
2286     DirEntryStack   aStack;
2287     ByteString      aPfad( rPfad );
2288     do
2289     {
2290         // den Namen vor dem ersten "/" abspalten,
2291         // falls '/' am Anfang, ist der Name '/',
2292         // der Rest immer ohne die fuehrenden '/'.
2293         // den ersten '/' suchen
2294         sal_uInt16 nPos;
2295         for ( nPos = 0;
2296               nPos < aPfad.Len() && aPfad.GetChar(nPos) != '/';
2297               nPos++ )
2298             /* do nothing */;
2299 
2300             // ist der Name die Root des aktuellen Drives?
2301         if ( nPos == 0 && aPfad.Len() > 0 && ( aPfad.GetChar(0) == '/' ) )
2302         {
2303             // Root-Directory des aktuellen Drives
2304             aStack.Push( new DirEntry( FSYS_FLAG_ABSROOT ) );
2305         }
2306         else
2307         {
2308             // den Namen ohne Trenner abspalten
2309             aName = aPfad.Copy( 0, nPos );
2310 
2311                         // stellt der Name die aktuelle Directory dar?
2312             if ( aName == "." )
2313                 /* do nothing */;
2314 
2315 #ifdef UNX
2316                         // stellt der Name das User-Dir dar?
2317                         else if ( aName == "~" )
2318                         {
2319                                 DirEntry aHome( String( (const char *) getenv( "HOME" ), osl_getThreadTextEncoding()) );
2320                                 for ( sal_uInt16 n = aHome.Level(); n; --n )
2321                                         aStack.Push( new DirEntry( aHome[ (sal_uInt16) n-1 ] ) );
2322                         }
2323 #endif
2324 
2325                 // stellt der Name die Parent-Directory dar?
2326             else if ( aName == ".." )
2327             {
2328                 // ist nichts, ein Parent oder eine relative Root
2329                 // auf dem Stack?
2330                 if ( ( aStack.Count() == 0 ) ||
2331                      ( aStack.Top()->eFlag == FSYS_FLAG_PARENT ) )
2332                     // fuehrende Parents kommen auf den Stack
2333                     aStack.Push( new DirEntry( ByteString(), FSYS_FLAG_PARENT, eStyle ) );
2334 
2335                 // ist es eine absolute Root
2336                 else if ( aStack.Top()->eFlag == FSYS_FLAG_ABSROOT ) {
2337                     // die hat keine Parent-Directory
2338                     return FSYS_ERR_NOTEXISTS;
2339                 }
2340                 else
2341                     // sonst hebt der Parent den TOS auf
2342                     delete aStack.Pop();
2343             }
2344             else
2345             {
2346                 DirEntry *pNew = NULL;
2347                 // normalen Entries kommen auf den Stack
2348                                 pNew = new DirEntry( aName, FSYS_FLAG_NORMAL, eStyle );
2349                                 if ( !pNew->IsValid() )
2350                                 {
2351                                         aName = rPfad;
2352                                         ErrCode eErr = pNew->GetError();
2353                                         delete pNew;
2354                                         return eErr;
2355                                 }
2356                 aStack.Push( pNew );
2357                         }
2358         }
2359 
2360         // den Restpfad bestimmen
2361         aPfad.Erase( 0, nPos + 1 );
2362         while ( aPfad.Len() && ( aPfad.GetChar(0) == '/' ) )
2363             aPfad.Erase( 0, 1 );
2364     }
2365     while ( aPfad.Len() );
2366 
2367     // Haupt-Entry (selbst) zuweisen
2368     if ( aStack.Count() == 0 )
2369     {
2370         eFlag = FSYS_FLAG_CURRENT;
2371         aName.Erase();
2372     }
2373     else
2374     {
2375         eFlag = aStack.Top()->eFlag;
2376         aName = aStack.Top()->aName;
2377         delete aStack.Pop();
2378     }
2379 
2380     // die Parent-Entries vom Stack holen
2381     DirEntry** pTemp = &pParent;
2382     while ( aStack.Count() )
2383     {
2384         *pTemp = aStack.Pop();
2385         pTemp = &( (*pTemp)->pParent );
2386     }
2387 
2388     return FSYS_ERR_OK;
2389 }
2390 
2391 /*************************************************************************
2392 |*
2393 |*    DirEntry::MakeShortName()
2394 |*
2395 |*    Beschreibung
2396 |*    Ersterstellung    TLX
2397 |*    Letzte Aenderung  PB  21.08.97 (in CreateEntry_Impl())
2398 |*
2399 *************************************************************************/
2400 
2401 ErrCode CreateEntry_Impl( const DirEntry &rPath, DirEntryKind eKind )
2402 {
2403     // versuchen, anzulegen (ausser bei FSYS_KIND_ALL)
2404     ErrCode eErr = ERRCODE_NONE;
2405     if ( FSYS_KIND_FILE == eKind )
2406     {
2407         SvFileStream aStream( rPath.GetFull(), STREAM_STD_WRITE );
2408         aStream.WriteLine( "" );
2409         eErr = aStream.GetError();
2410     }
2411     else if ( FSYS_KIND_ALL != eKind )
2412         eErr = rPath.MakeDir() ? ERRCODE_NONE : ERRCODE_IO_UNKNOWN;
2413 
2414     // erfolgreich?
2415     if ( !rPath.Exists() )
2416         eErr = ERRCODE_IO_UNKNOWN;  // Doch was schiefgegangen ?
2417 
2418     // ggf. wieder l"oschen
2419     if ( FSYS_KIND_NONE == eKind )
2420         rPath.Kill();
2421 
2422     // Fehlercode zur?ckliefern
2423     return eErr;
2424 }
2425 
2426 sal_Bool IsValidEntry_Impl( const DirEntry &rPath,
2427                         const String &rLongName,
2428                         DirEntryKind eKind,
2429                         sal_Bool bIsShortened,
2430                         sal_Bool bUseDelim )
2431 {
2432     // Parameter-Pr"uefung
2433     DBG_ASSERT( eKind == FSYS_KIND_NONE || eKind == FSYS_KIND_ALL ||
2434                 eKind == FSYS_KIND_FILE || eKind == FSYS_KIND_DIR,
2435                 "invalid entry-kind" );
2436 
2437     // Alle von MSDOS erreichbaren FSYS_STYLES muessen den
2438     // MSDOS Filenamenanforderungen genuegen. Sonst wird probiert,
2439     // ob sich eine Datei des gewuenschten Names anlegen laesst.
2440     FSysPathStyle eStyle = DirEntry::GetPathStyle( rPath.GetDevice().GetName() );
2441     DirEntry aPath(rPath);
2442     DirEntry aName(rLongName, eStyle);
2443     if ( !aName.IsValid() || aName.Level() != 1 )
2444         return sal_False;
2445     aPath += aName;
2446     if ( 1 == aPath.Level() )
2447         return sal_False;
2448     if ( eStyle == FSYS_STYLE_FAT || eStyle == FSYS_STYLE_NWFS ||
2449          eStyle == FSYS_STYLE_UNKNOWN )
2450     {
2451         DirEntry aDosEntry( rLongName, FSYS_STYLE_FAT );
2452         if ( !aDosEntry.IsValid() )
2453             return sal_False;
2454     }
2455 
2456         // Pfad-Trenner sind nicht erlaubt (bei ungek"urzten auch nicht FSYS_SHORTNAME_DELIMITER)
2457         char cDelim = bUseDelim == 2 ? FSYS_SHORTNAME_DELIMITER : char(0);
2458     if (
2459          rLongName.Search(DirEntry::GetAccessDelimiter()) != STRING_NOTFOUND ||
2460          (!bIsShortened && rLongName.Search(cDelim) != STRING_NOTFOUND)
2461        )
2462     {
2463         return sal_False;
2464     }
2465 
2466     // MI: Abfrage nach 'CON:' etc. wird jetzt in Exists() mitgemacht
2467     if ( aPath.Exists() )
2468         return sal_False;
2469 
2470     return (ERRCODE_NONE == CreateEntry_Impl( aPath, eKind ));
2471 }
2472 
2473 //-------------------------------------------------------------------------
2474 
2475 #define MAX_EXT_FAT         3
2476 #define MAX_LEN_FAT         8
2477 #define INVALID_CHARS_FAT   "\\/\"':|^<>[]?* "
2478 
2479 #define MAX_EXT_MAC        16   // nur wegen sinnvoller Namensk"rzung
2480 #define MAX_LEN_MAC        31
2481 #define INVALID_CHARS_MAC   "\":"
2482 
2483 #define MAX_EXT_MAX       250
2484 #define MAX_LEN_MAX       255
2485 #define INVALID_CHARS_DEF   "\\/\"':|^<>?*"
2486 
2487 sal_Bool DirEntry::MakeShortName( const String& rLongName, DirEntryKind eKind,
2488                               sal_Bool bUseDelim, FSysPathStyle eStyle )
2489 {
2490 		String aLongName(rLongName);
2491 
2492         // Alle '#' aus den Dateinamen entfernen, weil das INetURLObject
2493         // damit Probleme hat. Siehe auch #51246#
2494         aLongName.EraseAllChars( '#' );
2495         ByteString bLongName(aLongName, osl_getThreadTextEncoding());
2496 
2497         // Auf Novell-Servern (wegen der rottigen Clients) nur 7bit ASCII
2498 
2499 		// HRO: #69627# Weg mit dem Scheiss. Wenn es Client gibt, die so einen
2500 		// BUG haben, dann muss halt der Client ersetzt werden, aber doch nicht das
2501 		// Office kastrieren !!!
2502 
2503 #if 0
2504         if ( FSYS_STYLE_NWFS == GetPathStyle( ImpGetTopPtr()->GetName() ) )
2505         {
2506                 for ( sal_uInt16 n = aLongName.Len(); n; --n )
2507                 {
2508                         short nChar = aLongName(n-1);
2509                         if ( nChar < 32 || nChar >= 127 )
2510                                 aLongName.Erase( n-1, 1 );
2511                 }
2512         }
2513 #endif
2514 
2515         // bei FSYS_KIND_ALL den alten Namen merken und abh"angen (rename)
2516         ByteString aOldName;
2517         if ( FSYS_KIND_ALL == eKind )
2518         {
2519             aOldName = ByteString(CutName(), osl_getThreadTextEncoding());
2520             aOldName = CMP_LOWER(aOldName);
2521         }
2522 
2523         // ist der Langname direkt verwendbar?
2524         if ( IsValidEntry_Impl( *this, aLongName, eKind, sal_False, bUseDelim ) )
2525         {
2526             operator+=( DirEntry(aLongName) );
2527             return sal_True;
2528         }
2529 
2530         // max L"angen feststellen
2531         sal_uInt16 nMaxExt, nMaxLen;
2532         if ( FSYS_STYLE_DETECT == eStyle )
2533             eStyle = DirEntry::GetPathStyle( GetDevice().GetName() );
2534         ByteString aInvalidChars;
2535         switch ( eStyle )
2536         {
2537             case FSYS_STYLE_FAT:
2538                 nMaxExt = MAX_EXT_FAT;
2539                 nMaxLen = MAX_LEN_FAT;
2540                 aInvalidChars = INVALID_CHARS_FAT;
2541                 break;
2542 
2543             case FSYS_STYLE_MAC:
2544                 nMaxExt = MAX_EXT_MAC;
2545                 nMaxLen = MAX_LEN_MAC;
2546                 aInvalidChars = INVALID_CHARS_MAC;
2547                 break;
2548 
2549             default:
2550                 nMaxExt = MAX_EXT_MAX;
2551                 nMaxLen = MAX_LEN_MAX;
2552                 aInvalidChars = INVALID_CHARS_DEF;
2553         }
2554 
2555         // Extension abschneiden und kuerzen
2556         ByteString aExt;
2557         ByteString aFName = bLongName;
2558         if ( FSYS_STYLE_MAC != eStyle )
2559         {
2560             DirEntry aUnparsed;
2561             aUnparsed.aName = bLongName;
2562             aExt = ByteString(aUnparsed.CutExtension(), osl_getThreadTextEncoding());
2563             aFName = aUnparsed.aName;
2564             if ( aExt.Len() > nMaxExt )
2565             {
2566                 char c = aExt.GetChar( aExt.Len() - 1 );
2567                 aExt.Erase(nMaxExt-1);
2568                 aExt += c;
2569             }
2570         }
2571 
2572         if ( FSYS_STYLE_FAT != eStyle )
2573         {
2574                 // ausser auf einem FAT-System geh"ort die Extension zur
2575                 // Maxl"ange. Muss also vorher mit dem Punkt abgezogen werden.
2576                 nMaxLen -= ( aExt.Len() + 1 );
2577         }
2578 
2579         // Name k"urzen
2580         ByteString aSName;
2581         for ( const char *pc = aFName.GetBuffer(); aSName.Len() < nMaxLen && *pc; ++pc )
2582         {
2583             if ( STRING_NOTFOUND == aInvalidChars.Search( *pc ) &&
2584                  (unsigned char) *pc >= (unsigned char) 32 &&
2585                  ( !aSName.Len() || *pc != ' ' || aSName.GetChar(aSName.Len()-1) != ' ' ) )
2586                 aSName += *pc;
2587         }
2588         aSName.EraseTrailingChars();
2589 
2590 		// HRO: #74246# Also cut leading spaces
2591 		aSName.EraseLeadingChars();
2592 
2593         if ( !aSName.Len() )
2594             aSName = "noname";
2595 
2596         // kommt dabei der alte Name raus?
2597         ByteString aNewName = aSName;
2598         if ( aExt.Len() )
2599             ( aNewName += '.' ) += aExt;
2600         operator+=( DirEntry(String(aNewName, osl_getThreadTextEncoding())) );
2601         if ( FSYS_KIND_ALL == eKind && CMP_LOWER(aName) == aOldName )
2602         if ( FSYS_KIND_ALL == eKind && CMP_LOWER(ByteString(GetName(), osl_getThreadTextEncoding())) == aOldName )
2603             return sal_True;
2604 
2605         // kann der gek"urzte Name direkt verwendet werden?
2606         if ( !Exists() && (ERRCODE_NONE == CreateEntry_Impl( *this, eKind )) )
2607             return sal_True;
2608 
2609         // darf '?##' verwendet werden, um eindeutigen Name zu erzeugen?
2610         if ( bUseDelim )
2611         {
2612                 // eindeutigen Namen per '?##' erzeugen
2613             aSName.Erase( nMaxLen-3 );
2614             if ( bUseDelim != 2 )
2615                         aSName += FSYS_SHORTNAME_DELIMITER;
2616             for ( int n = 1; n < 99; ++n )
2617             {
2618                 // Name zusammensetzen
2619                 ByteString aTmpStr( aSName );
2620                 aTmpStr += ByteString::CreateFromInt32(n);
2621                 if ( aExt.Len() )
2622                     ( aTmpStr += '.' ) += aExt;
2623 
2624                 // noch nicht vorhanden?
2625                 SetName( String(aTmpStr, osl_getThreadTextEncoding()) );
2626 
2627                 if ( !Exists() )
2628                 {
2629                     // Fehler setzen !!!
2630                     nError = CreateEntry_Impl( *this, eKind );
2631                     return (ERRCODE_NONE == nError);
2632                 }
2633             }
2634         }
2635 
2636         // keine ## mehr frei / ?## soll nicht verwendet werden
2637         nError = ERRCODE_IO_ALREADYEXISTS;
2638         return sal_False;
2639 }
2640 
2641 /*************************************************************************
2642 |*
2643 |*    DirEntry::CreatePath()
2644 |*
2645 |*    Beschreibung      FSYS.SDW
2646 |*    Ersterstellung    MI 26.04.91
2647 |*    Letzte Aenderung  MA 02.12.91
2648 |*
2649 *************************************************************************/
2650 
2651 sal_Bool DirEntry::MakeDir( sal_Bool bSloppy ) const
2652 {
2653     DBG_CHKTHIS( DirEntry, ImpCheckDirEntry );
2654 
2655         // Schnellpruefung, ob vorhanden
2656         if ( FileStat( *this ).IsKind( FSYS_KIND_DIR ) )
2657                 return sal_True;
2658         if ( bSloppy && pParent )
2659                  if ( FileStat( *pParent ).IsKind( FSYS_KIND_DIR ) )
2660                           return sal_True;
2661 
2662         const DirEntry *pNewDir = bSloppy ? pParent : this;
2663         if ( pNewDir )
2664         {
2665                 // den Path zum Dir erzeugen
2666                 if ( pNewDir->pParent && !pNewDir->pParent->MakeDir(sal_False) )
2667                         return sal_False;
2668 
2669                 // das Dir selbst erzeugen
2670                 if ( pNewDir->eFlag == FSYS_FLAG_ABSROOT ||
2671                          pNewDir->eFlag == FSYS_FLAG_ABSROOT ||
2672                          pNewDir->eFlag == FSYS_FLAG_VOLUME )
2673                         return sal_True;
2674                 else
2675                 {
2676                         //? nError = ???
2677                         if ( FileStat( *pNewDir ).IsKind( FSYS_KIND_DIR ) )
2678                                 return sal_True;
2679                         else
2680                         {
2681                                 FSysFailOnErrorImpl();
2682 								String aDirName(pNewDir->GetFull());
2683 #ifndef BOOTSTRAP
2684                                 FSysRedirector::DoRedirect( aDirName );
2685 #endif
2686                                 ByteString bDirName( aDirName, osl_getThreadTextEncoding() );
2687                                 bDirName = GUI2FSYS( bDirName );
2688 
2689 #ifdef WIN32
2690 								SetLastError(0);
2691 #endif
2692                                 sal_Bool bResult = (0 == _mkdir( (char*) bDirName.GetBuffer() ));
2693                                 if ( !bResult )
2694                                 {
2695                                     // Wer hat diese Methode const gemacht ?
2696 #ifdef WIN32
2697                                     ((DirEntry *)this)->SetError( Sys2SolarError_Impl(  GetLastError() ) );
2698 #else
2699                                     ((DirEntry *)this)->SetError( Sys2SolarError_Impl(  errno ) );
2700 #endif
2701                                 }
2702 
2703 								return bResult;
2704                         }
2705                 }
2706         }
2707         return sal_True;
2708 }
2709 
2710 /*************************************************************************
2711 |*
2712 |*    DirEntry::CopyTo()
2713 |*
2714 |*    Beschreibung      FSYS.SDW
2715 |*    Ersterstellung    MI 26.04.91
2716 |*    Letzte Aenderung  MI 07.08.96
2717 |*
2718 *************************************************************************/
2719 
2720 FSysError DirEntry::CopyTo( const DirEntry& rDest, FSysAction nActions ) const
2721 {
2722     DBG_CHKTHIS( DirEntry, ImpCheckDirEntry );
2723 
2724         if ( FSYS_ACTION_COPYFILE != (nActions & FSYS_ACTION_COPYFILE) )
2725 #ifdef UNX
2726     {
2727         // Hardlink anlegen
2728                 HACK(redirection missing)
2729 	ByteString aThis(GUI2FSYS(GetFull()), osl_getThreadTextEncoding());
2730 	ByteString aDest(GUI2FSYS(rDest.GetFull()), osl_getThreadTextEncoding());
2731         if (link( aThis.GetBuffer(), aDest.GetBuffer() ) == -1)
2732             return Sys2SolarError_Impl(  errno );
2733         else
2734             return FSYS_ERR_OK;
2735     }
2736 #else
2737         return FSYS_ERR_NOTSUPPORTED;
2738 #endif
2739 
2740         FileCopier fc(*this, rDest);
2741         return fc.Execute(nActions);
2742 }
2743 
2744 /*************************************************************************
2745 |*
2746 |*    DirEntry::MoveTo()
2747 |*
2748 |*    Beschreibung      FSYS.SDW
2749 |*    Ersterstellung    MI 26.04.91
2750 |*    Letzte Aenderung  HRO 24.03.99
2751 |*
2752 *************************************************************************/
2753 
2754 #if defined WNT || defined UNX || defined OS2
2755 
2756 FSysError DirEntry::MoveTo( const DirEntry& rNewName ) const
2757 {
2758     DBG_CHKTHIS( DirEntry, ImpCheckDirEntry );
2759 
2760 /*
2761     FileStat aSourceStat(*this);
2762     if ( !aSourceStat.IsKind(FSYS_KIND_FILE) )
2763         return FSYS_ERR_NOTAFILE;
2764 */
2765 
2766 	DirEntry aDest(rNewName);
2767 	FileStat aDestStat(rNewName);
2768 	if ( aDestStat.IsKind(FSYS_KIND_DIR ) )
2769 	{
2770 		aDest += String(aName, osl_getThreadTextEncoding());
2771 	}
2772 	if ( aDest.Exists() )
2773 	{
2774 		return FSYS_ERR_ALREADYEXISTS;
2775 	}
2776 
2777 #if defined(OS2)
2778 	if ( FileStat(*this).IsKind(FSYS_KIND_DIR) && aDest.GetPath() != GetPath() )
2779 	{
2780 		return FSYS_ERR_NOTSUPPORTED;
2781 	}
2782 #endif
2783 
2784         FSysFailOnErrorImpl();
2785         String aFrom( GetFull() );
2786 
2787 #ifndef BOOTSTRAP
2788         FSysRedirector::DoRedirect(aFrom);
2789 #endif
2790 
2791         String aTo( aDest.GetFull() );
2792 
2793 #ifndef BOOTSTRAP
2794         FSysRedirector::DoRedirect(aTo);
2795 #endif
2796 
2797 		ByteString bFrom(aFrom, osl_getThreadTextEncoding());
2798 		ByteString bTo(aTo, osl_getThreadTextEncoding());
2799         bFrom = GUI2FSYS(bFrom);
2800         bTo = GUI2FSYS(bTo);
2801 
2802 #ifdef WNT
2803 		// MoveTo nun atomar
2804         SetLastError(0);
2805 
2806 		DirEntry aFromDevice(String(bFrom, osl_getThreadTextEncoding()));
2807 		DirEntry aToDevice(String(bTo,osl_getThreadTextEncoding()));
2808 		aFromDevice.ToAbs();
2809 		aToDevice.ToAbs();
2810 		aFromDevice=aFromDevice.GetDevice();
2811 		aToDevice=aToDevice.GetDevice();
2812 
2813 		//Quelle und Ziel auf gleichem device?
2814 		if (aFromDevice==aToDevice)
2815 		{
2816 			// ja, also intra-device-move mit MoveFile
2817 			MoveFile( bFrom.GetBuffer(), bTo.GetBuffer() );
2818 			// MoveFile ist buggy bei cross-device operationen.
2819 			// Der R?ckgabewert ist auch dann sal_True, wenn nur ein Teil der Operation geklappt hat.
2820 			// Zudem zeigt MoveFile unterschiedliches Verhalten bei unterschiedlichen NT-Versionen.
2821         	return Sys2SolarError_Impl( GetLastError() );
2822         }
2823         else
2824         {
2825         	//nein, also inter-device-move mit copy/delete
2826 	        FSysError nCopyError = CopyTo(rNewName, FSYS_ACTION_COPYFILE);
2827 
2828 			DirEntry aKill(String(bTo, osl_getThreadTextEncoding()));
2829 			FileStat aKillStat(String(bTo, osl_getThreadTextEncoding()));
2830 			if ( aKillStat.IsKind(FSYS_KIND_DIR ) )
2831 			{
2832 				aKill += String(aName, osl_getThreadTextEncoding());
2833 			}
2834 
2835 	        if (nCopyError==FSYS_ERR_OK)
2836 	        {
2837 	            if (Kill()==FSYS_ERR_OK)
2838 	            {
2839 	            	return FSYS_ERR_OK;
2840 	            }
2841 	            else
2842 	            {
2843 	            	aKill.Kill();
2844 					return FSYS_ERR_ACCESSDENIED;
2845 	            }
2846 	        }
2847 	        else
2848 	        {
2849             	aKill.Kill();
2850 	            return nCopyError;
2851 	        }
2852 		}
2853 #else
2854 		// #68639#
2855 		// on some nfs connections rename with from == to
2856 		// leads to destruction of file
2857         if ( ( aFrom != aTo ) && ( 0 != rename( bFrom.GetBuffer(), bTo.GetBuffer() ) ) )
2858 #if !defined(UNX) && !defined(OS2)
2859             return Sys2SolarError_Impl( GetLastError() );
2860 #else
2861         {
2862                 if( errno == EXDEV )
2863 // cross device geht latuernich nicht mit rename
2864                 {
2865                         FILE *fpIN  = fopen( bFrom.GetBuffer(), "r" );
2866                         FILE *fpOUT = fopen( bTo.GetBuffer(), "w" );
2867                         if( fpIN && fpOUT )
2868                         {
2869                                 char pBuf[ 16384 ];
2870                                 int nBytes, nWritten, nErr = 0;
2871                                 errno = 0;
2872                                 while( ( nBytes = fread( pBuf, 1, sizeof(pBuf), fpIN ) ) && ! nErr )
2873                                 {
2874 									nWritten = fwrite( pBuf, 1, nBytes, fpOUT );
2875 									// Fehler im fwrite     ?
2876 									if( nWritten < nBytes )
2877 									{
2878 										nErr = errno;
2879 										break;
2880 									}
2881                                 }
2882                                 fclose( fpIN );
2883                                 fclose( fpOUT );
2884                                 if ( nErr )
2885 								{
2886 									unlink( bTo.GetBuffer() );
2887 									return Sys2SolarError_Impl( nErr );
2888 								}
2889 								else
2890 								{
2891 									unlink( bFrom.GetBuffer() );
2892 								}
2893                         }
2894                         else
2895 						{
2896 							return Sys2SolarError_Impl( EXDEV );
2897 						}
2898                 }
2899                 else
2900 				{
2901 					return Sys2SolarError_Impl( errno );
2902 				}
2903         }
2904 #endif
2905 #endif
2906         return ERRCODE_NONE;
2907 }
2908 
2909 #endif
2910 
2911 /*************************************************************************
2912 |*
2913 |*    DirEntry::Kill()
2914 |*
2915 |*    Beschreibung      FSYS.SDW
2916 |*    Ersterstellung    MI 26.04.91
2917 |*    Letzte Aenderung  MI 07.08.96
2918 |*
2919 *************************************************************************/
2920 
2921 FSysError DirEntry::Kill(  FSysAction nActions ) const
2922 {
2923     DBG_CHKTHIS( DirEntry, ImpCheckDirEntry );
2924 
2925         FSysError eError = FSYS_ERR_OK;
2926         FSysFailOnErrorImpl();
2927 
2928         // Name als doppelt 0-terminierter String
2929         String aTmpName( GetFull() );
2930 #ifndef BOOTSTRAP
2931         FSysRedirector::DoRedirect( aTmpName );
2932 #endif
2933 		ByteString bTmpName( aTmpName, osl_getThreadTextEncoding());
2934         bTmpName = GUI2FSYS(bTmpName);
2935 
2936         char *pName = new char[bTmpName.Len()+2];
2937         strcpy( pName, bTmpName.GetBuffer() );
2938         pName[bTmpName.Len()+1] = (char) 0;
2939 
2940 		//read-only files sollen auch geloescht werden koennen
2941 		sal_Bool isReadOnly = FileStat::GetReadOnlyFlag(*this);
2942 		if (isReadOnly)
2943 		{
2944 			FileStat::SetReadOnlyFlag(*this, sal_False);
2945 		}
2946 
2947         // directory?
2948         if ( FileStat( *this ).IsKind(FSYS_KIND_DIR) )
2949         {
2950                 // Inhalte recursiv loeschen?
2951                 if ( FSYS_ACTION_RECURSIVE == (nActions & FSYS_ACTION_RECURSIVE) )
2952                 {
2953                         Dir aDir( *this, FSYS_KIND_DIR|FSYS_KIND_FILE );
2954                         for ( sal_uInt16 n = 0; eError == FSYS_ERR_OK && n < aDir.Count(); ++n )
2955                         {
2956                                 const DirEntry &rSubDir = aDir[n];
2957                                 DirEntryFlag flag = rSubDir.GetFlag();
2958                                 if ( flag != FSYS_FLAG_CURRENT && flag != FSYS_FLAG_PARENT )
2959                                         eError = rSubDir.Kill(nActions);
2960                         }
2961                 }
2962 
2963                 // das Dir selbst loeschen
2964 #ifdef WIN32
2965 				SetLastError(0);
2966 #endif
2967                 if ( eError == FSYS_ERR_OK && 0 != _rmdir( (char*) pName ) )
2968 				//
2969                 {
2970                         // falls L"oschen nicht ging, CWD umsetzen
2971 #ifdef WIN32
2972 					eError = Sys2SolarError_Impl( GetLastError() );
2973 #else
2974 					eError = Sys2SolarError_Impl( errno );
2975 #endif
2976                         if ( eError )
2977                         {
2978                                 GetPath().SetCWD();
2979 #ifdef WIN32
2980 								SetLastError(0);
2981 #endif
2982 								if (_rmdir( (char*) pName) != 0)
2983 								{
2984 #ifdef WIN32
2985 									eError = Sys2SolarError_Impl( GetLastError() );
2986 #else
2987 									eError = Sys2SolarError_Impl( errno );
2988 #endif
2989 								}
2990 								else
2991 								{
2992 									eError = FSYS_ERR_OK;
2993 								}
2994                         }
2995                 }
2996         }
2997         else
2998         {
2999                 if ( FSYS_ACTION_USERECYCLEBIN == (nActions & FSYS_ACTION_USERECYCLEBIN) )
3000                 {
3001 #ifdef OS2
3002                         eError = ApiRet2ToSolarError_Impl( DosDelete( (PSZ) pName ) );
3003 #elif defined(WNT)
3004                         SHFILEOPSTRUCT aOp;
3005                         aOp.hwnd = 0;
3006                         aOp.wFunc = FO_DELETE;
3007                         aOp.pFrom = pName;
3008                         aOp.pTo = 0;
3009                         aOp.fFlags = FOF_ALLOWUNDO|FOF_SILENT|FOF_NOCONFIRMATION;
3010                         aOp.hNameMappings = 0;
3011                         aOp.lpszProgressTitle = 0;
3012                         eError = Sys2SolarError_Impl( SHFileOperation( &aOp ) );
3013 #else
3014                         eError = ERRCODE_IO_NOTSUPPORTED;
3015 #endif
3016                 }
3017                 else
3018                 {
3019 #ifdef WIN32
3020 					SetLastError(0);
3021 #endif
3022 					if ( 0 != _unlink( (char*) pName ) )
3023 					{
3024 #ifdef WIN32
3025 						eError = Sys2SolarError_Impl( GetLastError() );
3026 #else
3027 						eError = Sys2SolarError_Impl( errno );
3028 #endif
3029 					}
3030 					else
3031 					{
3032 						eError = ERRCODE_NONE;
3033 					}
3034                 }
3035         }
3036 
3037 		//falls Fehler, originales read-only flag wieder herstellen
3038 		if ( isReadOnly && (eError!=ERRCODE_NONE) )
3039 		{
3040 			FileStat::SetReadOnlyFlag(*this, isReadOnly);
3041 		}
3042 
3043 		delete[] pName;
3044         return eError;
3045 }
3046 
3047 /*************************************************************************
3048 |*
3049 |*    DirEntry::Contains()
3050 |*
3051 |*    Beschreibung      ob rSubEntry direkt oder indirect in *this liegt
3052 |*    Ersterstellung    MI 20.03.97
3053 |*    Letzte Aenderung  MI 20.03.97
3054 |*
3055 *************************************************************************/
3056 
3057 sal_Bool DirEntry::Contains( const DirEntry &rSubEntry ) const
3058 {
3059     DBG_ASSERT( IsAbs() && rSubEntry.IsAbs(), "must be absolute entries" );
3060 
3061         sal_uInt16 nThisLevel = Level();
3062     sal_uInt16 nSubLevel = rSubEntry.Level();
3063     if ( nThisLevel < nSubLevel )
3064     {
3065         for ( ; nThisLevel; --nThisLevel, --nSubLevel )
3066             if ( (*this)[nThisLevel-1] != rSubEntry[nSubLevel-1] )
3067                 return sal_False;
3068         return sal_True;
3069     }
3070     return sal_False;
3071 }
3072 
3073 /*************************************************************************
3074 |*
3075 |*    DirEntry::Level()
3076 |*
3077 |*    Beschreibung      FSYS.SDW
3078 |*    Ersterstellung    MI 03.03.92
3079 |*    Letzte Aenderung  MI 03.03.92
3080 |*
3081 *************************************************************************/
3082 
3083 sal_uInt16 DirEntry::Level() const
3084 {
3085     DBG_CHKTHIS( DirEntry, ImpCheckDirEntry );
3086 
3087     sal_uInt16 nLevel = 0;
3088     const DirEntry *pRes = this;
3089     while ( pRes )
3090     {
3091         pRes = pRes->pParent;
3092         nLevel++;
3093     }
3094 
3095     return nLevel;
3096 }
3097 
3098 /*************************************************************************
3099 |*
3100 |*    DirEntry::ConvertNameToSystem()
3101 |*
3102 |*    Beschreibung
3103 |*    Ersterstellung    DV 29.03.96
3104 |*    Letzte Aenderung  DV 29.03.96
3105 |*
3106 *************************************************************************/
3107 
3108 String DirEntry::ConvertNameToSystem( const String &rName )
3109 {
3110     return rName;
3111 }
3112 
3113 /*************************************************************************
3114 |*
3115 |*    DirEntry::ConvertSystemToName()
3116 |*
3117 |*    Beschreibung
3118 |*    Ersterstellung    DV 29.03.96
3119 |*    Letzte Aenderung  DV 29.03.96
3120 |*
3121 *************************************************************************/
3122 
3123 String DirEntry::ConvertSystemToName( const String &rName )
3124 {
3125     return rName;
3126 }
3127 
3128 /*************************************************************************
3129 |*
3130 |*    DirEntry::IsValid()
3131 |*
3132 |*    Beschreibung
3133 |*    Ersterstellung    MI  18.09.93
3134 |*    Letzte Aenderung  TPF 18.09.98
3135 |*
3136 *************************************************************************/
3137 
3138 sal_Bool DirEntry::IsValid() const
3139 {
3140         return (nError == FSYS_ERR_OK);
3141 }
3142 
3143 /*************************************************************************
3144 |*
3145 |*    DirEntry::IsRFSAvailable()
3146 |*
3147 |*    Beschreibung
3148 |*    Ersterstellung    TPF 21.10.98
3149 |*    Letzte Aenderung  TPF 21.10.98
3150 |*
3151 *************************************************************************/
3152 
3153 sal_Bool DirEntry::IsRFSAvailable()
3154 {
3155     return sal_False;
3156 }
3157 
3158 /*************************************************************************
3159 |*
3160 |*    IsLongNameOnFAT()
3161 |*
3162 |*    Beschreibung      ?berpr?ft , ob das DirEntry einen langen
3163 |*                      Filenamen auf einer FAT-Partition enth?lt (EAs).
3164 |*                      (eigentlich nur f?r OS2 interessant)
3165 |*    Ersterstellung    TPF 02.10.98
3166 |*    Letzte Aenderung  TPF 01.03.1999
3167 |*
3168 *************************************************************************/
3169 
3170 sal_Bool DirEntry::IsLongNameOnFAT() const
3171 {
3172         // FAT-System?
3173         DirEntry aTempDirEntry(*this);
3174         aTempDirEntry.ToAbs();
3175         if (DirEntry::GetPathStyle(aTempDirEntry.GetDevice().GetName().GetChar(0)) != FSYS_STYLE_FAT)
3176         {
3177             return sal_False;       // nein, also false
3178         }
3179 
3180         // DirEntry-Kette auf lange Dateinamen pr?fen
3181         for( sal_uInt16 iLevel = this->Level(); iLevel > 0; iLevel-- )
3182         {
3183             const DirEntry& rEntry = (const DirEntry&) (*this)[iLevel-1];
3184             String  aBase( rEntry.GetBase() );
3185             String  aExtension( rEntry.GetExtension() );
3186 
3187             if (aBase.Len()>8)  // Name > 8?
3188             {
3189                 return sal_True;
3190             }
3191 
3192             if (aExtension.Len()>3) // Extension > 3?
3193             {
3194                 return sal_True;
3195             }
3196         }
3197         return sal_False;
3198 }
3199 
3200 //========================================================================
3201 
3202 #if defined(DBG_UTIL)
3203 
3204 void FSysTest()
3205 {
3206 }
3207 
3208 #endif
3209 
3210